{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "treated-drama",
   "metadata": {},
   "source": [
    "# The Annotated GAT (PPI)\n",
    "\n",
    "This notebook is the 2nd part of the series, please check out **\"The Annotated GAT (Cora)\"** notebook for a more gentle introduction to GAT. ❤️\n",
    "\n",
    "The idea of this notebook is to explain how you can use GAT in an **inductive setting**. \n",
    "\n",
    "I'll be using the **PPI (protein-protein interaction) dataset** in this notebook.\n",
    "\n",
    "Here is a representation of the 3D structure of the protein [myoglobin](https://en.wikipedia.org/wiki/Protein) (not like you need to know anything about proteins in order to follow along this notebook it's just that they are beautiful and I love them 🍗❤️)!\n",
    "\n",
    "<img src=\"data/readme_pics/protein.png\" alt=\"protein schematic\" align=\"center\"/> <br/>\n",
    "\n",
    "In this notebook you'll get the answers to these questions:\n",
    "\n",
    "✅ How to load and visualize the PPI dataset? <br/>\n",
    "✅ How to train/use GAT on PPI (multi-label classification problem)? <br/>\n",
    "✅ How to visualize different GAT's properties? (mainly attention) <br/>\n",
    "\n",
    "Awesome, let's start!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "described-classroom",
   "metadata": {},
   "outputs": [],
   "source": [
    "# I always like to structure my imports into Python's native libs,\n",
    "# stuff I installed via conda/pip and local file imports (but we don't have those here)\n",
    "\n",
    "import json\n",
    "import os\n",
    "import enum\n",
    "\n",
    "# Visualization related imports\n",
    "import matplotlib.pyplot as plt\n",
    "import networkx as nx\n",
    "from networkx.readwrite import json_graph\n",
    "import igraph as ig\n",
    "\n",
    "# Main computation libraries\n",
    "import numpy as np\n",
    "\n",
    "# Deep learning related imports\n",
    "import torch\n",
    "from torch.utils.data import DataLoader, Dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "modified-electric",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "    Contains constants needed for data loading and visualization.\n",
    "\n",
    "\"\"\"\n",
    "\n",
    "\n",
    "# Supported datasets - only PPI in this notebook\n",
    "class DatasetType(enum.Enum):\n",
    "    PPI = 0\n",
    "\n",
    "    \n",
    "class GraphVisualizationTool(enum.Enum):\n",
    "    IGRAPH = 0\n",
    "\n",
    "\n",
    "# We'll be dumping and reading the data from this directory\n",
    "DATA_DIR_PATH = os.path.join(os.getcwd(), 'data')\n",
    "PPI_PATH = os.path.join(DATA_DIR_PATH, 'ppi')\n",
    "PPI_URL = 'https://data.dgl.ai/dataset/ppi.zip'  # preprocessed PPI data from Deep Graph Library\n",
    "\n",
    "#\n",
    "# PPI specific constants\n",
    "#\n",
    "\n",
    "PPI_NUM_INPUT_FEATURES = 50\n",
    "PPI_NUM_CLASSES = 121"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bacterial-failing",
   "metadata": {},
   "source": [
    "Note that some parts of this notebook overlap with \"The Annotated GAT (Cora)\" notebook, so you may see some redundancy. 🙏\n",
    "\n",
    "With that out of the way we've got the level 1 unlocked (Data 📜). 😍 Let's go!"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "processed-mortality",
   "metadata": {},
   "source": [
    "# Part 1: Understanding your data (become One with the data 📜❤️)\n",
    "\n",
    "I'll be using the PPI dataset as the running example in this notebook.\n",
    "\n",
    "Having said that, you may wonder, what's the difference between `transductive` and `inductive` setting? If you're not familiar with GNNs this may appear as a weird concept. But it's quite simple actually.\n",
    "\n",
    "**Transductive** - you have a single graph (like Cora) you split some **nodes** (and not graphs) into train/val/test training sets. While you're training you'll be using only the labels from your training nodes. BUT. During the forward prop, by the nature of how spatial GNNs work, you'll be aggregating the feature vectors from your neighbors and **some of them may belong to val or even test sets!** The main point is - you **ARE NOT** using their label information but you **ARE** using the structural information and their features.\n",
    "\n",
    "**Inductive** - you're probably much more familiar with this one if you come from the computer vision or NLP background. You have a set of training graphs, a separate set of val graphs and of course a separate set of test graphs.\n",
    "\n",
    "Having explained that let's jump into the code and let's load and visualize PPI."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "variable-insulin",
   "metadata": {},
   "outputs": [],
   "source": [
    "# First let's define this simple function for loading PPI's graph data\n",
    "\n",
    "def json_read(path):\n",
    "    with open(path, 'r') as file:\n",
    "        data = json.load(file)\n",
    "\n",
    "    return data"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "outdoor-child",
   "metadata": {},
   "source": [
    "Now let's see how we can load PPI!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "metropolitan-programming",
   "metadata": {},
   "outputs": [],
   "source": [
    "def load_graph_data(training_config, device):\n",
    "    dataset_name = training_config['dataset_name'].lower()\n",
    "    should_visualize = training_config['should_visualize']\n",
    "\n",
    "    if dataset_name == DatasetType.PPI.name.lower():  # Protein-Protein Interaction dataset\n",
    "\n",
    "        # Instead of checking PPI in, I'd rather download it on-the-fly the first time it's needed (lazy execution ^^)\n",
    "        if not os.path.exists(PPI_PATH):  # download the first time this is ran\n",
    "            os.makedirs(PPI_PATH)\n",
    "\n",
    "            # Step 1: Download the ppi.zip (contains the PPI dataset)\n",
    "            zip_tmp_path = os.path.join(PPI_PATH, 'ppi.zip')\n",
    "            download_url_to_file(PPI_URL, zip_tmp_path)\n",
    "\n",
    "            # Step 2: Unzip it\n",
    "            with zipfile.ZipFile(zip_tmp_path) as zf:\n",
    "                zf.extractall(path=PPI_PATH)\n",
    "            print(f'Unzipping to: {PPI_PATH} finished.')\n",
    "\n",
    "            # Step3: Remove the temporary resource file\n",
    "            os.remove(zip_tmp_path)\n",
    "            print(f'Removing tmp file {zip_tmp_path}.')\n",
    "\n",
    "        # Collect train/val/test graphs here\n",
    "        edge_index_list = []\n",
    "        node_features_list = []\n",
    "        node_labels_list = []\n",
    "\n",
    "        # Dynamically determine how many graphs we have per split (avoid using constants when possible)\n",
    "        num_graphs_per_split_cumulative = [0]\n",
    "\n",
    "        # Small optimization \"trick\" since we only need test in the playground.py\n",
    "        splits = ['test'] if training_config['ppi_load_test_only'] else ['train', 'valid', 'test']\n",
    "\n",
    "        for split in splits:\n",
    "            # PPI has 50 features per node, it's a combination of positional gene sets, motif gene sets,\n",
    "            # and immunological signatures - you can treat it as a black box (I personally have a rough understanding)\n",
    "            # shape = (NS, 50) - where NS is the number of (N)odes in the training/val/test (S)plit\n",
    "            # Note: node features are already preprocessed\n",
    "            node_features = np.load(os.path.join(PPI_PATH, f'{split}_feats.npy'))\n",
    "\n",
    "            # PPI has 121 labels and each node can have multiple labels associated (gene ontology stuff)\n",
    "            # SHAPE = (NS, 121)\n",
    "            node_labels = np.load(os.path.join(PPI_PATH, f'{split}_labels.npy'))\n",
    "\n",
    "            # Graph topology stored in a special nodes-links NetworkX format\n",
    "            nodes_links_dict = json_read(os.path.join(PPI_PATH, f'{split}_graph.json'))\n",
    "            # PPI contains undirected graphs with self edges - 20 train graphs, 2 validation graphs and 2 test graphs\n",
    "            # The reason I use a NetworkX's directed graph is because we need to explicitly model both directions\n",
    "            # because of the edge index and the way GAT implementation #3 works\n",
    "            collection_of_graphs = nx.DiGraph(json_graph.node_link_graph(nodes_links_dict))\n",
    "            # For each node in the above collection, ids specify to which graph the node belongs to\n",
    "            graph_ids = np.load(os.path.join(PPI_PATH, F'{split}_graph_id.npy'))\n",
    "            num_graphs_per_split_cumulative.append(num_graphs_per_split_cumulative[-1] + len(np.unique(graph_ids)))\n",
    "\n",
    "            # Split the collection of graphs into separate PPI graphs\n",
    "            for graph_id in range(np.min(graph_ids), np.max(graph_ids) + 1):\n",
    "                mask = graph_ids == graph_id  # find the nodes which belong to the current graph (identified via id)\n",
    "                graph_node_ids = np.asarray(mask).nonzero()[0]\n",
    "                graph = collection_of_graphs.subgraph(graph_node_ids)  # returns the induced subgraph over these nodes\n",
    "                print(f'Loading {split} graph {graph_id} to CPU. '\n",
    "                      f'It has {graph.number_of_nodes()} nodes and {graph.number_of_edges()} edges.')\n",
    "\n",
    "                # shape = (2, E) - where E is the number of edges in the graph\n",
    "                # Note: leaving the tensors on CPU I'll load them to GPU in the training loop on-the-fly as VRAM\n",
    "                # is a scarcer resource than CPU's RAM and the whole PPI dataset can't fit during the training.\n",
    "                edge_index = torch.tensor(list(graph.edges), dtype=torch.long).transpose(0, 1).contiguous()\n",
    "                edge_index = edge_index - edge_index.min()  # bring the edges to [0, num_of_nodes] range\n",
    "                edge_index_list.append(edge_index)\n",
    "                # shape = (N, 50) - where N is the number of nodes in the graph\n",
    "                node_features_list.append(torch.tensor(node_features[mask], dtype=torch.float))\n",
    "                # shape = (N, 121), BCEWithLogitsLoss doesn't require long/int64 so saving some memory by using float32\n",
    "                node_labels_list.append(torch.tensor(node_labels[mask], dtype=torch.float))\n",
    "\n",
    "                if should_visualize:\n",
    "                    plot_in_out_degree_distributions(edge_index.numpy(), graph.number_of_nodes(), dataset_name)\n",
    "                    visualize_graph(edge_index.numpy(), node_labels[mask], dataset_name)\n",
    "\n",
    "        #\n",
    "        # Prepare graph data loaders\n",
    "        #\n",
    "\n",
    "        # Optimization, do a shortcut in case we only need the test data loader\n",
    "        if training_config['ppi_load_test_only']:\n",
    "            data_loader_test = GraphDataLoader(\n",
    "                node_features_list[num_graphs_per_split_cumulative[0]:num_graphs_per_split_cumulative[1]],\n",
    "                node_labels_list[num_graphs_per_split_cumulative[0]:num_graphs_per_split_cumulative[1]],\n",
    "                edge_index_list[num_graphs_per_split_cumulative[0]:num_graphs_per_split_cumulative[1]],\n",
    "                batch_size=training_config['batch_size'],\n",
    "                shuffle=False\n",
    "            )\n",
    "            return data_loader_test\n",
    "        else:\n",
    "\n",
    "            data_loader_train = GraphDataLoader(\n",
    "                node_features_list[num_graphs_per_split_cumulative[0]:num_graphs_per_split_cumulative[1]],\n",
    "                node_labels_list[num_graphs_per_split_cumulative[0]:num_graphs_per_split_cumulative[1]],\n",
    "                edge_index_list[num_graphs_per_split_cumulative[0]:num_graphs_per_split_cumulative[1]],\n",
    "                batch_size=training_config['batch_size'],\n",
    "                shuffle=True\n",
    "            )\n",
    "\n",
    "            data_loader_val = GraphDataLoader(\n",
    "                node_features_list[num_graphs_per_split_cumulative[1]:num_graphs_per_split_cumulative[2]],\n",
    "                node_labels_list[num_graphs_per_split_cumulative[1]:num_graphs_per_split_cumulative[2]],\n",
    "                edge_index_list[num_graphs_per_split_cumulative[1]:num_graphs_per_split_cumulative[2]],\n",
    "                batch_size=training_config['batch_size'],\n",
    "                shuffle=False  # no need to shuffle the validation and test graphs\n",
    "            )\n",
    "\n",
    "            data_loader_test = GraphDataLoader(\n",
    "                node_features_list[num_graphs_per_split_cumulative[2]:num_graphs_per_split_cumulative[3]],\n",
    "                node_labels_list[num_graphs_per_split_cumulative[2]:num_graphs_per_split_cumulative[3]],\n",
    "                edge_index_list[num_graphs_per_split_cumulative[2]:num_graphs_per_split_cumulative[3]],\n",
    "                batch_size=training_config['batch_size'],\n",
    "                shuffle=False\n",
    "            )\n",
    "\n",
    "            return data_loader_train, data_loader_val, data_loader_test\n",
    "    else:\n",
    "        raise Exception(f'{dataset_name} not yet supported.')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "tropical-watts",
   "metadata": {},
   "source": [
    "Nice, there are is this `GraphDataLoader` object that we still haven't defined. We need it in order to load batches of PPI graphs into GAT.\n",
    "\n",
    "Here we go:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "dental-frank",
   "metadata": {},
   "outputs": [],
   "source": [
    "class GraphDataLoader(DataLoader):\n",
    "    \"\"\"\n",
    "    When dealing with batches it's always a good idea to inherit from PyTorch's provided classes (Dataset/DataLoader).\n",
    "\n",
    "    \"\"\"\n",
    "    def __init__(self, node_features_list, node_labels_list, edge_index_list, batch_size=1, shuffle=False):\n",
    "        graph_dataset = GraphDataset(node_features_list, node_labels_list, edge_index_list)\n",
    "        # We need to specify a custom collate function, it doesn't work with the default one\n",
    "        super().__init__(graph_dataset, batch_size, shuffle, collate_fn=graph_collate_fn)\n",
    "\n",
    "\n",
    "class GraphDataset(Dataset):\n",
    "    \"\"\"\n",
    "    This one just fetches a single graph from the split when GraphDataLoader \"asks\" it\n",
    "\n",
    "    \"\"\"\n",
    "    def __init__(self, node_features_list, node_labels_list, edge_index_list):\n",
    "        self.node_features_list = node_features_list\n",
    "        self.node_labels_list = node_labels_list\n",
    "        self.edge_index_list = edge_index_list\n",
    "\n",
    "    # 2 interface functions that need to be defined are len and getitem so that DataLoader can do it's magic\n",
    "    def __len__(self):\n",
    "        return len(self.edge_index_list)\n",
    "\n",
    "    def __getitem__(self, idx):  # we just fetch a single graph\n",
    "        return self.node_features_list[idx], self.node_labels_list[idx], self.edge_index_list[idx]\n",
    "\n",
    "\n",
    "def graph_collate_fn(batch):\n",
    "    \"\"\"\n",
    "    The main idea here is to take multiple graphs from PPI as defined by the batch size\n",
    "    and merge them into a single graph with multiple connected components.\n",
    "\n",
    "    It's important to adjust the node ids in edge indices such that they form a consecutive range. Otherwise\n",
    "    the scatter functions in the implementation 3 will fail.\n",
    "\n",
    "    :param batch: contains a list of edge_index, node_features, node_labels tuples (as provided by the GraphDataset)\n",
    "    \"\"\"\n",
    "\n",
    "    edge_index_list = []\n",
    "    node_features_list = []\n",
    "    node_labels_list = []\n",
    "    num_nodes_seen = 0\n",
    "\n",
    "    for features_labels_edge_index_tuple in batch:\n",
    "        # Just collect these into separate lists\n",
    "        node_features_list.append(features_labels_edge_index_tuple[0])\n",
    "        node_labels_list.append(features_labels_edge_index_tuple[1])\n",
    "\n",
    "        edge_index = features_labels_edge_index_tuple[2]  # all of the components are in the [0, N] range\n",
    "        edge_index_list.append(edge_index + num_nodes_seen)  # very important! translate the range of this component\n",
    "        num_nodes_seen += len(features_labels_edge_index_tuple[1])  # update the number of nodes we've seen so far\n",
    "\n",
    "    # Merge the PPI graphs into a single graph with multiple connected components\n",
    "    node_features = torch.cat(node_features_list, 0)\n",
    "    node_labels = torch.cat(node_labels_list, 0)\n",
    "    edge_index = torch.cat(edge_index_list, 1)\n",
    "\n",
    "    return node_features, node_labels, edge_index"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "marine-mechanics",
   "metadata": {},
   "source": [
    "The idea is simple 💡 (as all things should be 😜). In order to pass a batch of graphs into GAT we do the following:\n",
    "\n",
    "1. During the preprocessing step, we map the edge index from the original range into the [0, N] range, where N is the number of nodes in a given graph. Example: the third graph in the training split may contain nodes [3500, ..., 5000] and thus originally its edge index would be in that very same range, e.g.: [3500, 4232], [3808, 4232], ...]. By subtracting the min element, 3500 in this case, we bring the edge index into [0, 1500] range.\n",
    "\n",
    "---\n",
    "\n",
    "2. In the `graph_collate_fn` function, edge indices are in the normalized range i.e. [0, N]. What we do is we shift them such that we end up with a single graph that actually consists out of multiple smaller PPI graphs which are not connected with each other i.e. they represent separate [connected components](https://www.geeksforgeeks.org/connected-components-in-an-undirected-graph/). \n",
    "\n",
    "---\n",
    "\n",
    "**Example:** let's say we have 3 graphs in a batch and all 3 edge indices are in the [0, 1000] range. What we'll do is the following: we'll leave the first graph without any modification, we'll shift second graph's range to [1000, 2000], because we had 1000 nodes in the graph that came before, and we'll shift the third graph's range into [2000, 3000], again because we had 2000 nodes that came before this graph. So the final edge index will have nodes in the [0, 3000] range and we treat that as a single graph with multiple connected components. 🤓\n",
    "\n",
    "Nice, finally let's try and load it. We should also analyze the shapes - that's always a good idea."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "unexpected-mercury",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Loading train graph 1 to CPU. It has 1767 nodes and 34085 edges.\n",
      "Loading train graph 2 to CPU. It has 1377 nodes and 31081 edges.\n",
      "Loading train graph 3 to CPU. It has 2263 nodes and 61907 edges.\n",
      "Loading train graph 4 to CPU. It has 2339 nodes and 67769 edges.\n",
      "Loading train graph 5 to CPU. It has 1578 nodes and 37740 edges.\n",
      "Loading train graph 6 to CPU. It has 1021 nodes and 19237 edges.\n",
      "Loading train graph 7 to CPU. It has 1823 nodes and 46153 edges.\n",
      "Loading train graph 8 to CPU. It has 2488 nodes and 72878 edges.\n",
      "Loading train graph 9 to CPU. It has 591 nodes and 8299 edges.\n",
      "Loading train graph 10 to CPU. It has 3312 nodes and 109510 edges.\n",
      "Loading train graph 11 to CPU. It has 2401 nodes and 66619 edges.\n",
      "Loading train graph 12 to CPU. It has 1878 nodes and 48146 edges.\n",
      "Loading train graph 13 to CPU. It has 1819 nodes and 47587 edges.\n",
      "Loading train graph 14 to CPU. It has 3480 nodes and 110234 edges.\n",
      "Loading train graph 15 to CPU. It has 2794 nodes and 88112 edges.\n",
      "Loading train graph 16 to CPU. It has 2326 nodes and 62188 edges.\n",
      "Loading train graph 17 to CPU. It has 2650 nodes and 79714 edges.\n",
      "Loading train graph 18 to CPU. It has 2815 nodes and 88335 edges.\n",
      "Loading train graph 19 to CPU. It has 3163 nodes and 97321 edges.\n",
      "Loading train graph 20 to CPU. It has 3021 nodes and 94359 edges.\n",
      "Loading valid graph 21 to CPU. It has 3230 nodes and 100676 edges.\n",
      "Loading valid graph 22 to CPU. It has 3284 nodes and 104758 edges.\n",
      "Loading test graph 23 to CPU. It has 3224 nodes and 103872 edges.\n",
      "Loading test graph 24 to CPU. It has 2300 nodes and 63628 edges.\n",
      "********************\n",
      "torch.Size([3021, 50]) torch.float32\n",
      "torch.Size([3021, 121]) torch.float32\n",
      "torch.Size([2, 94359]) torch.int64\n"
     ]
    }
   ],
   "source": [
    "# Let's just define dummy visualization functions for now - just to stop Python interpreter from complaining!\n",
    "# We'll define them in a moment, properly, I swear.\n",
    "\n",
    "def plot_in_out_degree_distributions():\n",
    "    pass\n",
    "\n",
    "def visualize_graph():\n",
    "    pass\n",
    "\n",
    "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")  # checking whether you have a GPU\n",
    "\n",
    "config = {\n",
    "    'dataset_name': DatasetType.PPI.name,\n",
    "    'should_visualize': False,\n",
    "    'batch_size': 1,\n",
    "    'ppi_load_test_only': False  # small optimization for loading test graphs only, we won't use it here\n",
    "}\n",
    "\n",
    "data_loader_train, data_loader_val, data_loader_test = load_graph_data(config, device)\n",
    "# Let's fetch a single batch from the train graph data loader\n",
    "node_features, node_labels, edge_index = next(iter(data_loader_train))\n",
    "\n",
    "print('*' * 20)\n",
    "print(node_features.shape, node_features.dtype)\n",
    "print(node_labels.shape, node_labels.dtype)\n",
    "print(edge_index.shape, edge_index.dtype)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "expanded-friday",
   "metadata": {},
   "source": [
    "Nice! Analyzing the shapes we see the following:\n",
    "1. This specific PPI train graph (batch size = 1) has 3021 nodes \n",
    "2. Each node has **50 features** (check out [data_loading.py](https://github.com/gordicaleksa/pytorch-GAT/blob/main/utils/data_loading.py) for much more detail)\n",
    "3. PPI has **121 classes** and each node can have multiple classes associated with it (multi-label classification dataset)\n",
    "4. This graph has 94359 edges (including the self edges)! (Compare this to 13k edges in Cora)\n",
    "5. PPI has **20 train** graphs, **2 validation** graphs and **2 test** graphs\n",
    "\n",
    "Additionally the edge index is of `int 64` type. Why? Well it's a constraint that PyTorch is imposing upon us. `index_select` functions require torch.long (i.e. 64 bit integer) and we use those in GAT implementation 3 - that's it.\n",
    "\n",
    "node_labels can be `float32` because `nn.BCEWithLogitsLoss` doesn't require long/int64 type (compare that to `nn.CrossEntropyLoss`, that we used in the Cora notebook, which does require int64s). So we can save up 2x memory! Not bad.\n",
    "\n",
    "---\n",
    "\n",
    "On the \"side note\", it's always a **good idea to test your code as you're progressing with your project.** \n",
    "\n",
    "Data loading is completely orthogonal to the rest of this notebook so we can test it, standalone, and make sure the shapes and datatypes make sense. I use this strategy while developing projects like this one (and in general).\n",
    "\n",
    "I start with data, I add the loading functionality, I add some visualizations and only then do I usually start developing the deep learning model itself.\n",
    "\n",
    "Visualizations are a huge bonus, so let's develop them."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "mediterranean-advancement",
   "metadata": {},
   "source": [
    "# Visualizing your data 🔮👓\n",
    "\n",
    "Let's start by understanding the degree distribution of nodes in PPI - i.e. how many input/output edges do nodes have, a certain measure of connectedness of the graph.\n",
    "\n",
    "Run the following cell:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "welcome-construction",
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_in_out_degree_distributions(edge_index, num_of_nodes, dataset_name):\n",
    "    \"\"\"\n",
    "        Note: It would be easy to do various kinds of powerful network analysis using igraph/networkx, etc.\n",
    "        I chose to explicitly calculate only the node degree statistics here, but you can go much further if needed and\n",
    "        calculate the graph diameter, number of triangles and many other concepts from the network analysis field.\n",
    "\n",
    "    \"\"\"\n",
    "    if isinstance(edge_index, torch.Tensor):\n",
    "        edge_index = edge_index.cpu().numpy()\n",
    "        \n",
    "    assert isinstance(edge_index, np.ndarray), f'Expected NumPy array got {type(edge_index)}.'\n",
    "\n",
    "    # Store each node's input and output degree (they're the same for undirected graphs such as Cora/PPI)\n",
    "    in_degrees = np.zeros(num_of_nodes, dtype=np.int)\n",
    "    out_degrees = np.zeros(num_of_nodes, dtype=np.int)\n",
    "\n",
    "    # Edge index shape = (2, E), the first row contains the source nodes, the second one target/sink nodes\n",
    "    # Note on terminology: source nodes point to target/sink nodes\n",
    "    num_of_edges = edge_index.shape[1]\n",
    "    for cnt in range(num_of_edges):\n",
    "        source_node_id = edge_index[0, cnt]\n",
    "        target_node_id = edge_index[1, cnt]\n",
    "\n",
    "        out_degrees[source_node_id] += 1  # source node points towards some other node -> increment it's out degree\n",
    "        in_degrees[target_node_id] += 1  # similarly here\n",
    "\n",
    "    hist = np.zeros(np.max(out_degrees) + 1)\n",
    "    for out_degree in out_degrees:\n",
    "        hist[out_degree] += 1\n",
    "\n",
    "    fig = plt.figure(figsize=(12,8), dpi=100)  # otherwise plots are really small in Jupyter Notebook\n",
    "    fig.subplots_adjust(hspace=0.6)\n",
    "\n",
    "    plt.subplot(311)\n",
    "    plt.plot(in_degrees, color='red')\n",
    "    plt.xlabel('node id'); plt.ylabel('in-degree count'); plt.title('Input degree for different node ids')\n",
    "\n",
    "    plt.subplot(312)\n",
    "    plt.plot(out_degrees, color='green')\n",
    "    plt.xlabel('node id'); plt.ylabel('out-degree count'); plt.title('Out degree for different node ids')\n",
    "\n",
    "    plt.subplot(313)\n",
    "    plt.plot(hist, color='blue')\n",
    "    plt.xlabel('node degree'); plt.ylabel('# nodes for a given out-degree'); plt.title(f'Node out-degree distribution for {dataset_name} dataset')\n",
    "    plt.xticks(np.arange(0, len(hist), 20.0))\n",
    "\n",
    "    plt.grid(True)\n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "sublime-franklin",
   "metadata": {},
   "source": [
    "Brilliant, let's now visualize PPI's degree distributions!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "optimum-clearance",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA+0AAAKxCAYAAAAmfE0rAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/Il7ecAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzddbjcVPoH8O/bFocWd4oWXZzFfixuiy26LO6yOCzutkhxKIsWdynFrUDRUkqh1IGWtlSou9x723vf3x8nYc7kJpkkk7nJzP1+nmeeeyeTSc5Ez5tjoqogIiIiIiIiovxpk3UCiIiIiIiIiMgfg3YiIiIiIiKinGLQTkRERERERJRTDNqJiIiIiIiIcopBOxEREREREVFOMWgnIiIiIiIiyikG7UREREREREQ5xaCdiIiIiIiIKKcYtBMRERERERHlFIN2IiKqaSJyooioiKyVdVoqTUQuEZHfRKRRRPpllIaeItLTer+Ws/1P9My3r4j0E5E65/OlnenHichQEZkvItNbMu21wtme16e4vJEi8lSE+VrNuUZE1JIYtBMREYCiDPc2WacFAERkcRG5XkR2zTot1UBE9gbQGcDXAE4CcGW2KQomIssBeAXAPABnAzgOwBwR2RDAUwCGAzgNwOlZpbEUEdnYOT7XyjotRERU29plnQAiIqIAiwO4zvm/Z4bpqBa7A2gCcIqqNmSdGMsoAIsBmG9N+yuApQBco6o93InOA5o2AM5X1WEtmMYkNoY5PnsCGJlpSipvA5hji4iIMsCSdiIiogoQkXYisnALrnJFAPPSCtjFWKzc5ahRp6qN1uQVnb/TPbMHTU9MRJZIa1mtlarWq+r80nMSEVElMGgnIqJAIvKUiMwWkdVEpLvz/yQRuVNE2lrzue2WLxaRC0VklIjME5HPReQvnmUWtXn2rGukuzwAk5yPrnOWXbKdrohsIiKfOuseIyJXI+BeJyJ/F5EvRWSOiMwSkXdFZBOf+Y4QkcFO2+uBInKInVaf33+BiAwHUA9TGgsR2VBEXhORqc5yvheRg3zWtbSI3Csio0WkXkSGichlIhJ6vxYRhakSv4S1rU50PmsnIteIyHBnmSNF5BYRWcSzjJEi8o6I7CMi38NUXT+jxHpPd5Y7T0S+E5G/+cyzlic9PQE87Xzcx/nM3Z43ONMnefd3lP1lHa/rish7IjILwPPOZ22cfTPI2QcTROQREVkmYDvs5PymOjH9BBxvzXMigFedt59Z23zXkG0V6Vxy5l1CRO6yjoOfnWNLPPMtIiL3OMuZJSJvicjqAetfTUSecH53vbMdTg5Kr882ecozLdK5JiLbiMiHIjLZmXeEiDwRZb1ERGSwejwREZXSFsCHAHoDuBjAngD+A9Pu+CHPvMfDVHt+EMCiAM4H8KmIbKqqE2KscxKAfzvLfwNAN2d6/6AviMjKAD6DubfdBmAOTJvoeT7zHgcTOH4I4DKYqvj/BvCViGypqiOd+fYH8DKAAQCuALAMgK4AxgYk4ySY3/0oTNA+1Qksv3a+46brnwC6i8hhqvqGs67FAXwOYDUAjwD4HcCOAG4FsAqAC4J+O0yb8NMBbAvgVGfaN87fxwGcAOA1AHcB2M75LRsBOMSznA0AvOis/zEAPwetUEROceb7BsC9ANYB8BaAqQBGh6T1v85yTwdwLYARMMdSd5jj5xCYfTEbzv6Our8c7Zz5voI5Xuc60x8BcCKAJwHcD2BtAOcA2FJE/s9TkrwezPbq6qz3ZABPiUhfVR0E4AtnGecBuAXAEOd7QxCu5LnkBOZvAdjNWX8/APsAuAPm2LjQWt7jAI4F8ALMftgdwLvelYrISgC+BaAAusCcX38H0FVE2qvqvSXS7V1epHNNRFYE8JGzvttgalCsBeDQOOsjImr1VJUvvvjiiy++ABPQKIBtrGlPOdOu8cz7A4DvrfdrOfPNBbCaNX1bZ/rd1rSeAHr6rP8pACOt98s7370+Yvrvcebf1pq2AkygoADWcqYtCWAagEc931/JmfdRa1p/mAB0SWvaLs7yRvr8/hkAVvAst4eznEWsaQITyP9iTbsaJlDt5Pn+rQAWAFijxO9/CsBsz7TNnXQ95pl+hzN9N2vaSGfaPhG29UIAJgD4EcDC1vTTnGX0tKa52+bEsGPNmX69M315a1qc/fWU8/1bPfPu5Ew/2jN9H+90azv8zXMc1QG405p2uDPfrhGPTzdtpc6lfzjzXeWZ71WYduXrevbtg575nofnvIEJ7scBWM4z74vONlysRNpHAngqwbl2sN9+5osvvvjiK96L1eOJiCiKhz3vv4QpWfXqrqp/lkKr6ncwpYr7VTBtrv0AfOus013/JDjVoy17AVgawIsisrz7AtDopHU3ABCRVQFsCuAZVZ1tLfNzmJJ3P68764SzjGVhSj9fAbCUta7lYEpcO4nIas7sR8Bs12medPWAKaHdOfYWKWz3uz3T73L+7u+ZPkJVP4yw3G1g2p8/rMVt6J+CeXCRpkj7y8NbA+QIJ10fe5bRF+ZBiXcZg1X1S/eNs09/hv8xH1epc2k/mN92v2e+u2Ae9vzdmg8+891rv3FK7g8D8Lbz1v79HwLoAGCrmL8h6rk23fl7gIgsFHMdRETkYPV4IiIqpc4ORB3TYKqKe/3qM+0XmOrglbYmTBDn5a3i3cn5+2nAcmZaywMAv17Mh8E/0Bnheb8eTKB1k/PysyJM1flOADZDoS2/33xxrQlTOlv0G1R1vJgx0Nf0zO9Nf9hyAc/+VtX5IvJbgnSGibq/XAsAjPFZRgcAEwOW4d22v/vME3TMxxHlXFoTwDhVneWZb4j1ufu3CaZqvc17vK8A89DjdAQPoRf32Ip6rn0O4HWYXvYvdPoz6A7gBVWtj7lOIqJWi0E7ERGV0lh6llgUJpD1auszrRLcWmbHARjv8/mCMpbtbT/vrutOmFJNP8OseT+GGWvdzy9lpEsjztes/X8OxN1f9arqHZ6sDUzAfkzAOryBdNAx73fcxpH2uRSFu/2eQ6EDQK/AviLKoaoK4HAR2R7AgTDNEZ4A8B8R2d6uwUJERMEYtBMRUZo6+UxbH8XjWE+DfzVjb6lv1EDTNSpg/Rt43rslkxPVGiM8YHmAKS338pvmxy11nl9iXW66lowwXxyjYIK2TrA6SXM6Jlsahd+YZLlwlvtnCbhTBXptAD8lXK6fqPur1DL2BPC1qqb1YCLu8RnVKAB7ishSntL2Da3P3b9tAKyL4hJu7/E+CcAsAG1TPLainmsAAFX9FqYjvKtE5GiYavT/gmlrT0REJbBNOxERpelgq402RGRbmN7K37fmGQ5gQxFZwZpvcwD/51mW2+v30hHX/R6A7Z11ustdAc1LVz+EqVJ9pV87WzddqjoOwEAAx4vIktbnu8C0dS9JVSfCdLx3hoisErQuxysAdhCRfXzmW1pEkjxof8/5e4Fn+kXO32Y9jUf0PUwweKYUj0V/IqLvr6gi7a8SXoGpyXGNz/fbicjSCdI1x/mb5Lth3oNJ6zme6RfCPChwzyX373me+S6w36hqI0wV9cPEM/wiEHn7+aWx5LkmIst4h6mD6Q0fABYBERFFwpJ2IiJK0zCYYbgegsmUXwBgCoqrfD8BEzR+KCJdYdrTnglgEID27kyqOk9EBgM4UkR+gRlKbKCqDgxYd2eYKtQfiMh9KAxDNQqmrbi73Jki8m8AzwL4QURegglAO8J0zPY1CgHTlQDeBPC1iDwJ0/b4HJhg/s9AvoSzYYYfGyAij8GUvq8EYAcAq8P0Ag6YHt0PAvCOMyZ2XwBLwDwgOBymF/bJEdfp/tafRORpAKc7gennMD36nwDTaeBncZZnLXe+My73IzBD+r0MU8J+Egq1C1IRc38FLeNzEXkEwBUisgXMMGTzYUqLj4AZmvC1mEnrB1Pd/TIR6QAzxN+nzoOacrwNM5zaf0VkLZhaC3vD9Cp/r6oOd35TPxF5EcBZzvq/AbAH/GuBXA7T2V5v5xgcDGBZmH4Z9nT+jyPSuQZznJ0lIm/APKxbCmaEgZkoPFAiIqISGLQTEVGanoHpHOsCmGD8OwDnqOof7gyqOkREjgdwI0yv5oNhAoCjAezqWd6pAB6AGWJqYQA3wATMzajqHyKymzP/5TAPCx6GGeqqq2feF0RknDPfJTAPGMbC9OT9pDXf2yJyFMxQZLfBdLx2IkwwskmUDaKqg0VkG5jOuE6E6Tl+IsxwaTda8811SvGvhAkkj4cJbn5xvpu0V/ZTYQLpE2HGQB8PM4zcDQmX56b3URFpC7P97oDpUf8gBHe4V866Iu2vEss4U0T6AjgDZmz1BTDNNp6DCfzjpmm8iJwJM+Z9V5jS8d0Q3Nld1OU2ichBMMfGkTAPQkbC/O67PLOfDPMA4xiY4dU+hXmQMdqzzAlOqfi1MGOknwVzfgyCGfc+bhqjnmvuQ6J/wTyomgFzTThGVaN2ekhE1OqJ6SOEiIgoOadEcASAS1T1zoyTU3Ei0g/AJFXdK+u0EBERUW1jm3YiIqIAIrKQty25iOwKU6W9ZwZJIiIiolaG1eOJiIiCrQagh4g8B1P1d0OY9vfjYaoDExEREVUUg3YiIqJg02A6hDsVwAowHW69C+ByVZ2SZcKIiIiodWCbdiIiIiIiIqKcYpt2IiIiIiIiopxi0E5ERERERESUU2zTDkBEBMCqAGZlnRYiIiIiIiJqNZYCME5D2q0zaDdWBTAm60QQERERERFRq7M6gLFBHzJoN2YBwOjRo9G+ffus00JEREREREQ1bubMmVhjjTWAEjW+GbRb2rdvz6CdiIiIiIiIcoMd0RERERERERHlFIN2IiIiIiIiopxi0E5ERERERNSajB0LHHYY0LNn1imhCBi0ExERERERtSanngp06wbstlvWKaEIGLQTERERERG1JiNHZp0CioFBOxEREREREVFOMWgnIiIiIiIiyikG7UREREREREQ5xaCdiIiIiIiIKKcYtBMRERERERHlVKZBu4j8W0T6i8hM59VLRP5ufb6oiDwoIlNEZLaIvC4iK3mW0VFE3hWRuSIyUUTuEJF2Lf9riIiIiIiIiNKVdUn7GACXA9gawDYAPgXwpohs4nx+D4ADARwBYBcAqwLo5n5ZRNoCeBfAwgB2BHACgBMB3NgyySciotxZsADo2hUYPjzrlBARERGVTVQ16zQUEZGpAC4B8BqASQCOVtXXnM82BDAEwA6q+q1TKv8OgFVVdYIzz5kAbgewgqo2RFxnewAzZsyYgfbt26f+m4iIqAXddx9wwQXm/5zd44iIiHJho42AoUPN/7xXZmbmzJno0KEDAHRQ1ZlB82Vd0v4nEWkrIv8CsASAXjCl7wsB6OHOo6pDAfwOYAdn0g4ABrgBu+NDAO0BbIIAIrKIiLR3XwCWSvXHEBFRdr74IusUEBEREaUm86BdRDYVkdkA6gE8DOAQVR0MYGUADao63fOVCc5ncP5O8Pkc1jx+rgAww3qNSfwDiIiIiIiIiCokdtAuIseLyCI+0xcWkeMTpOFnAFsA2A7AQwCeFpGNEywnjlsBdLBeq1d4fURERERERESxJSlpfxIm0PVayvksFlVtUNVhqtpXVa8A8BOA8wGMB7CwiCzt+cpKzmdw/q7k8zmsefzWWa+qM90XgFlx001ERDnFtnlERERUQ5IE7QLAL0e0OkxV83K1AbAIgL4A5gPY488Vi2wAoCNMm3c4fzcVkRWt7+8FYCaAwSmkhYiIiIiIiCgzkcczF5EfYYJ1BfCJiCywPm4LYG0AH8RZuYjcCuB9mM7llgJwNIBdAeyjqjNEpCuAu50e5WcCeABAL1X91lnERzDB+bMicilMO/abATyoqvVx0kJERERERESUN5GDdgDdnb9bwPTQPtv6rAHASACvx1z/igCeAbAKTCl9f5iA/WPn8wsBNDnLXcRZ71nul1W1UUQOgGkL3wvAHABPA7g2ZjqIiIiIiIiIcidy0K6qNwCAiIwE8LKq1pW7clU9pcTndQDOdl5B84wCsF+5aSEiIiIiImoV2P9LVYlT0g4AUNWnAdNbPExJeRvP57+nkzQiIiIiIiKi1i120C4inQA8AWBH70cw7d3bppAuIiIiIiIiolYvdtAO4CkACwAcAOAP+PckT0RERERERERlShK0bwFga1UdmnJaiIiIysd2ekRERFRDkozTPhjA8mknhIiIiIiIiIiKJQnaLwPQWUR2FZHlRKS9/Uo7gUREREREREStVZLq8T2cv594prMjOiIiIiIiIqIUJQnad0s9FURERERERETUTJJx2j+vREKIiIiIiIiIqFiScdp3DvtcVb9InhwiIirL7NnAkktmnQoiIiIiSkmS6vE9fabZ4+uwTTsRURYuvBC4917gs8+AXXfNOjVERERElIIkvccv43mtCGBfAH0A7J1e0oiIKJZ77zV/r7gi02RkjuO0ExERUQ1J0qZ9hs/kj0WkAcDdALYuO1VERERERERElKikPcgEABukuDwiIiIiIiKiVi1JR3SbeScBWAXA5QD6pZAmIiIiIiIiIkKyjuj6wXQ8J57p3wI4udwEEREREREREZGRJGhf2/O+CcAkVa1LIT1ERETlYUd0REREVEOSdEQ3qhIJISKilDBoJSIiIqoZiTqiE5FdRORtERnmvN4Skb+lnTgiIspQQwMwbFjWqSAiIiJq1WIH7SJyLIAeAOYCuN95zQPwiYgcnW7yiIgoNvF2OZLQbrsBnToB772XzvJaCmsaEBERUQ1JUtJ+FYBLVfVIVb3feR0J03v8Nekmj4iIMvPNN+bvY49lmw4iIiKiVixJ0L4OgLd9pr+F5p3UERHVrnvvBQ4/HFiwIOuUFGNJMxEREVHNSBK0jwawh8/0PZ3PiIhahwsvBF5/3byIiIiIiCogyZBvdwG4X0S2AODUncT/ATgRwPnpJIuIqIrMnp11Coql1aadiIiIahNr5VWVJEO+PSQi4wH8B8A/nclDABypqm+mmTgiaqVUTdXzTTYB9t4769QQEREREWUmSUk7VPUNAG+knBYiIuPTT4GLLjL/V8OT4LyVbFfDNiMiIiKiSJIM+fZXEdnOZ/p2IrJNOskiolbtt9+yTgERERERUS4k6YjuQQBr+ExfzfmMiKg8jY1Zp6C65a3kv6WxpgERERHVkCRB+8YAfvCZ/qPzGRFReaotaM9bkMyglYiIiKhmJAna6wGs5DN9FQCxBisWkStEpI+IzBKRiSLSXUQ28MyzqIg8KCJTRGS2iLwuIit55ukoIu+KyFxnOXeISKL2+kSUA3kb95yIiIiIKCNJgvaPANwqIh3cCSKyNIBbAHwcc1m7wFSp3x7AXgAWAvCRiCxhzXMPgAMBHOHMvyqAbta62wJ4F8DCAHYEcALM8HM3xkwLEeVFU1PWKSAiIiIiyoUkpdEXA/gCwCgR+dGZtgWACQCOi7MgVd3Xfi8iJwKYCGBrAF84DwZOAXC0qn7qzHMSgCEisr2qfgtgb5hq+Xuq6gQA/UTkGgC3i8j1qtqQ4DcSUZaqrXp83uStuj4RERERJRa7pF1VxwLYDMClAAYD6AvgfACbquroMtPjlt5Pdf5uDVP63sNa/1AAvwPYwZm0A4ABTsDu+hBAewCb+K1ERBYRkfbuC8BSZaabiNJUbdXj8xYks007ERERUc1IOk77HACPppkQEWkD4F4AX6vqQGfyygAaVHW6Z/YJzmfuPBN8Poc1j9cVAK4rJ71EVEEsaSciIiIiApCsTXulPAjgLwD+1QLruhWmVN99rd4C6ySiqBi0ExERUZY+/BB4772sU0EEIGFJe9pEpAuAAwDsrKpjrI/GA1hYRJb2lLav5HzmzrOtZ5ErWZ81o6r1ML3gu+tPnngiSh+D9vK09msamwcQEVE56uqAfZ2ut6ZPBzp0CJ2dqNIyLWkXowuAQwDsrqojPLP0BTAfwB7WdzYA0BFAL2dSLwCbisiK1vf2AjATps09EVUbBu3lYdBKRESUXH194f/Zs7NLB5Ej65L2BwEcDeAfAGaJiNsGfYaqzlPVGSLSFcDdIjIVJhB/AEAvp+d4wAxBNxjAsyJyKUw79psBPOiUqBNRtbGDdtX8lxznPX1EREREVLUSlbSLyNIicqqI3CoiyzrTthKR1WIu6t8wbcp7AvjDeh1pzXMhgHcAvA4z1Nx4AIe6H6pqI0zV+kaYUvfnADwD4Nr4v4yIcsEbtBMRUevw44/AG29knQoiolyJXdIuIpvBDME2A8BaAB6DGaLtUJhq68dHXZaqliyeUtU6AGc7r6B5RgHYL+p6iSjn7CHfmpqANnnqM7MKsOSfiKrVVluZv336ANtsk21aiAAWHlAuJMkJ3w3gKVXtBKDOmv4egJ1TSRURtW52SXtTU3bpCGPfxPMWJDODQUTVbsiQrFNARJQbSYL2vwJ4xGf6WASPi05EFF3UoH3WLGD06Mqnxw8DYyIiotpk5z3y9mCeWqUkQXs9gPY+09cHMKm85BBRJHktfU5L1KB9xRWBjh2B33+vfJq88rwP0s5gMMNCRNQ6dOkC7L+/GfKsNcvzPZ5apSRB+1sArhWRhZz3KiIdAdwO01kcEVXSbbcBK6wADB2adUoqxw7aw4Z/czMVX35Z2fT4yXNJe9ppy/Nv9VNt6aXkPvkEuO46DhNZi/iwMBvnngu89x7wxBNZpyRbvI9QziQJ2v8DYEkAEwEsBuBzAMMAzAJwVXpJIyJfV1wBTJ0KXHRR1impnGpo086qc0TZ23NP4MYbgRdeyDolRLVl1qysU5At+x7PAJ5yIHbv8ao6A8BeIrITgM1gAvgfVLVH2okjohC13KO6t/f4POJNnCg/Ro5Mf5mqps+Mjh3TXzYR5Zt9j89rPoRalcS5flX9CsATAO5gwE6UgVoO2qutpP2yy/JV8yEvJf98sEEtpRLH/JlnAmuuyWrCWcnLdYxaJ5a0U87EzvWLSBsRuUZExgKYDWBtZ/pNInJK2gkkogAM2rNlp+uPP4B77gEm5aQvzrQzGEkyzz/8AKyyCvDkk+mmhchPJQK8Rx81f6+5Jv1lE+Vda39owpJ2ypkkuf6rAZwI4FIADdb0gQBOTSFNRBRFLQft1XCz9AuM7Wr9rd0xxwATJgAnn5x1Sqg1qOXrYWuVJGhUBc44w3TYSlQOO++R13wItSpJ7nLHAzhdVZ8HYHfX+hOADVNJFVW3GTOAm28Gfv0165TUtlrOpObpZjlpkn8ask5X3vEBBrWkWr4eUnR9+pgaEldckXVKqh9L2v3/p8qYODHrFORekrvcajC9xfstayGf6dTaXHihqU646aZZp6S21XIm1Q6IsxzKqU8fMxb8/vs3/8zvJp6XTE4e0pGHNFDrUcnjLWzZTU1Ajx7AlCmVWz9FN2dO1imgWpGnwoNad+edwEormb8UKEmufzCAv/lMPxzAj+Ulh2qCO2Z2fX226ah1rSVoz/Jm2aWL+fvBB80/y/NNPA+lAlkG7Xn4/dSyKnm8hR1PTz0F7LUXsOWWlVt/a8UHf9mqxPZXNUPWVgN2RNdyLrmk+C/5SpLrvxFAFxG5zPn+oSLyGMwY7TemmTgiClHLQXs1tGnPa7ryoloz3L/9BvzI589VJ6vr4euvm7+jR5e3nO+/B8aMKT891Y7BUW27+GJgueUK502eVUM+hFqV2Hc5VX0TwIEA9gQwByZQ3wjAgar6cbrJI6JAtRy056WkPYxf5pIZzuq37rrAVlsB48dnnRKKo1ofEgHAkCHAX/8KrLFG1inJXl6v95SOu+82fy++ONt0RFEN+ZBaN2WKqTL/xx9ZpyQXYuX6RaSdiFwLYISq7qWqK6rq4qq6k6p+VKE0ErVu48YB/fo1n56noP3664Hbb09veXm5WZZqyxplWmtVjUHUvHmF/1nqmX/2Q7Ksjrc0HtR99135y6gV9jW03H3Kh6hUDnZEl72jjjJV5vfdN+uU5EKsXL+qLoAZ6q1dZZJD1ErNnh18U1htNdNe8uefi6fnJWgfNw644Qbg8suBhobS80cRJWhviZtoWKbRb/0M2guqMWi3e69ddNHs0kHR2CMUVOPx5mJAUFDuNdQ+Dng9Lk81n1NpyEvhQWv2sVOBu3//bNORE0ly/Z8A2CXthBC1Wv36AUstBZxySvh8339f/D4vQfvcuYX/08p8Ruk9PutSNpa0154JEwr/z5+fXTooGjtoz8v1sJo88ghw2WXlX7dVgW+/TafndgbtlBcsaaecSXKXex/AbSJyp4gcJSIH2a+0E0hU82691fx98snw+byBaR6fgqeVSYpb0l6pGypL2itPFXjmGeCHH7JOCYP2atNSQXser7VpOPNMoHNn/+ZXcTz+OLDDDsAee5SfpjSrx/N6XJ5aPe6jYkk75UySau7/c/5e5POZAmibPDlUE1r7hT6uqOOQezOleSxZSuvGFqXX1qxvoixpDxf1OtCjB3DCCeb/Sjx8UY2eFrt6fFpNPahyaqV6fBbq6gr/lzs8a9eu5m/v3uUtB0j3GsDrcXwsUS5g7/GUM7GDdlXNYaRAVMXsjGeY1hS0R3nCnfVNlEF7uKhB1MCB6a/bWwsjalrs6r0M2vOPQXty06cX/l944fKWlea9KM1rKK/H5oHMIotEnz/Nmg7VjuO0U87kMNdP1MrUUkl71N9SSl6C9rjV43ljL8hLhi/OPrGDQFaPzz97f6V17clCFteNadMK/5f7gKpSQTurx5fnl19Mh5pnnhn9O619m9lYPd7fq6+ajoeZ32lxsUvaReS8gI8UQB2AYQC+UNUqvoMStaCwm4GdEW3TpvgimcegPauSdnZElz/VHrSzpD3/aiVoz4Jd0l5u9fhaK2n/6Sdg2DDgsMPSS0sW3GFYH3kEePjhaN/heVTAjuj8/fOf5u9OOwEHHJBtWlqZJG3aLwSwAoDFAbiPapcBMBfAbAArAvhNRHZT1dGppJKoloXdJL0dLdmZkLY57D6iEkF7lN7jK4Ud0VU/Bu21y64NwXMvHgbtwbbYwvz95hvTwV61SnKP5HlUwJL2cOPHZ52CVifJlfZKAH0AdFLV5VR1OQDrA+gN4HwAHQGMB3BPaqkkqmVhbdrtTGmbNvkc4qgSN7a4Je1ZPAVnSXs6Kl0iz+rxtYsl7cnZ1eMrHbT36wf83/8BX3xRell5CNpdgwalk45qYp9HeaktlRV2REc5kyTXfzOAC1V1uDtBVYcBuBjArao6BsClAP4vnSRSVRs4ELjoImDSpKxTkl9hmc1qC9rTyjhXQ+/xLGkPl5cMH0vaa1dLBe1xa9xUgxkzCv9Xuk37PvuYUutddim9rDQfAqc55ntrwXtYQUsXDIwbV/wwLe9a4/mRsSS5/lXgX62+HYCVnf/HAVgqaaKoytkn8qabAvfcA5xxRnbpybuo1eNF8hm02+mvtd7jw25KLGkPl5cbOoP22tVSQXvYMVStQbv9QLjSJe32UIqllBu02/vD75iIs8xyr2FTpgB77QU891x5y0kqybGZ15L2V18F9tijZatkt2RJ+7RpwGqrAcsuW9n1pKlar31VLEmu/zMAj4jIlu4E5/+HAHzqTNoUwIjyk0c1o2/frFOQX1FL2pua8hm056F6PDuiy5+8ZPhYPb522furms+9LDK/9n0nStD+++/B81WqTbu9XerrTRrifN97TDz5pAmKvvoqWlrKvYZdfz3Qowdw3HHlLacl5fU8+uc/gU8/BS6+uOXW2ZIl7YMHV3b5VBOSXGlPATAVQF8RqReRegDfO9NOceaZDeA/6SSRYquvB157zTzljUPV/8I0YADQs2d5acpLBj6PwoL2Bx8s/O8N2vOiEtXj89KmvTV3RFfNT9GT9vrLkvbqUott2lvqvIsTtH//PbDmmsC22/p/Xqmg3f5/u+1MGr77Lvz7YaWjJ59smgUcfnj56Yxi8uSWWU+a7OOiksdi0jxh3HxtOdimPXuMHYrEvtKq6nhV3QvAxgCOcF4bq+reqjrBmeczVf0o3aRSZNdcAxxxBLDnnvG+d9BBwDbbNM/8bLYZsNtuwKhRydPUUifevHnAtdcCffoUpvXvD1x9NTBrVsukIa6wzOYttxTPZ2dS8xJUZVU9PuvhWGq9pL1W2oMyaK9dtRi0t9Q1xF5PqaD9+efN3/79/T9viaD9p5+K0xL3+7ao16Zyf1c13g/YY3pBVtuitW93CpRkyDfXbzBjsw9X1RwW/7Vi7k2tX7/o31EF3nnH/N+/P7Dlls3nGT7cPOlOoqWqcnfuDNx0k3m5mfXNNzd/Z8wAHnggnfWopheURM1seoP2PFzY6+uBbt0K79NKU6l2id51ZbEtWlNJe5JjPep30jqP3PSKpFPSzurx+VerQXup4TzTuP/ErR4fpiWqx7tK/e4o94Wo6Y26jYP2R9b3g3LbtGed/qxlVTDQ2Jif5o9hWmrY3bwUUOVA7KNCRBYXka4w47IPghniDSLygIhcHnNZO4vI2yIyTkRURA72fC4icqOI/CEi80Skh4h08syzrIg8LyIzRWS6iHQVkSXj/q6aMGFC8ptvlItzORfwlip1Gzgw+LMffkhnHZ07A6uuah5ipCFqlffGxvzdUK++Gvjvfwvvs2rTXqlt0Zo7osu6JkMcqqY20D77NE8rS9prl/1gpZaC9jCffAKsvDLw5pvlracagna/bVHpoD3uw8oJE0xhxrXXhqelWmT9MDxPstoWLXkty/u9PS819nIiyZX2VgCbA9gVQJ01vQeAI2MuawkAPwE4O+DzSwGcB+BMANsBmAPgQxFZ1JrneQCbANgLwAEAdgbwaMx0VL+hQ82N/B//SPb9KD2GlnNyt9SJ1xLruewy04PpmWems7xyS9rnzjW1JObNSyc9cTzzTPH7rNq0s6Q9fdX0W8aNAz7/HPj4Y2DmzOLPKh20/+9/wLHH1k7QWE1aquZRpe8rQW1nm5qAU04B7r67MG3PPU1v7AcfXN467eO11LFu//6DDjIdgtlqKWiP28HpHXcAo0eb2n1eeQ+I/KRRMHDRRf4PMapNSw/55spj30V+WiK/zaC9SJIr7cEAzlHVr2Cqx7sGAVg3zoJU9X1VvVpV3/B+JiIC4AIAN6vqm6raH8DxAFZ10gAR2QjAvgBOVdXeTprOBfAvEVk1aL0isoiItHdfqIXh6e691/z98MNk349SBTkqv5OspU68lqxS9Msv6Swn6jb2dkTnfu+UU4ADD8xmWD1vNc6sStpboiM6bzpaU0l7EknO+aTrDCsdUwV++81kqkuNgWuX3EYN2s8+2zRJspuJUMuo1erxrp49gSeeAP5TgX59k5a0v/22GXrLVkvV4+MGTGHXrKzvB0mup+U+DP/9dzPM7003VaaJUUsGcVl1RJfVtSzu8VKND6WqXJIr7QoA/AbdXALFQXy51oYZ972HO0FVZwDoDWAHZ9IOAKar6vfW93oAaIIpmQ9yBYAZ1mtMesnOSFi18CiCLhJJehJNcqNNS0sG7VGGn4mi3JL2l14yf599Np30xOHd3nfdlc5y43ZE1xI3VO9+ylvQnvYNtKU6ogt7MJKEX/X4bbc1JT+laseU06a91AMBSl+tB+3eWiNpas3V48OWYW+XKL8rrP+BrIP2JMotaa+zKuBW4++3ZVWbL6uS9ih5CAbqmUpypf0ewP7We3cPngqgV9kpKljZ+TvBM32C9dnK8DxAcDrFm2rN4+dWAB2s1+rlJjZzY8p87hAlaK9Em/bx44H11wduuCH5sqOsJ8/itGnPW0d03kzNk0+ms9w8Vo/3rqOS1eOnTo3/nbRvpi3VEZ0tje3nvZapFoYJChq6cuBAU811zpzCtLht2vNwPrY2lQjaFywwnZZmpaWOo9YctEctaY9yDQsL2isd4MyalX7fG2kO4VrtD9Jasl+XKJ3vVloeg/ZqzNNXUJIr7ZUAbhGRh2B6nz9fRD4CcBKAq9JMXKWoar2qznRfAHI6FlgM5Z5IaZa0x6ke/8orwK+/Atdfn87FIA89bj77rBkPNmpJXTX3Hl+ql+Ok8tJ7vH3ctlRJ+/33A8stZ/7GUcmgPcmyk9xsk2ZWvMdL3LRvuilw6aWm6q8rbmlHtWdQq1ElgvZttgGWXtr0k5CFLIL2cqsx2/fdcq9DWVePj9K/T9RlVXJfzpoFtG8PrLNOusstt6AmD8FnWlqyYMDeVgsWmAKtf/0r+EFzJUQ5d1s638mgvUiScdq/ArAFTMA+AMDeMKXdO6hq3xTTNt75u5Jn+krWZ+MBrGh/KCLtACxrzdM6pDn8S9D0SnREt7JVISKNNuJ5CNqPP96UOD/3XLT5o97Ygtq0l6t/f+C++5JVyarU9s5jSbtfCa5XGuk4//zivwDw5ZdA167h3/PL7DY0mI4Kk2ipm3Ma1ePDSoeS1hiIm+HM8iHad98BnTqV36N4tanE9dAdD/zdd6PNn8bDsizazsYJ2kudN/Z9oNxALc2S9qC0pNmmPavq8e5oOGPHBs+TRZv2qMdy0jxrpYK4bt2KH9oCLVvSbh+rjY2mr5SXXzajouRJSz+IYdBeJFGOW1WHq+ppqrqtqm6sqseq6oCU0zYCJvD+s8cTp9O47VCoht8LwNIisrX1vd1hflfvlNNT24KC80oH7fYNz3vBTCLsZtzSJ//kydHmS1rS/vjjppZCuTbfHLjgAuDhh+N/t1Il7Wl2RDd4sOmILIksStr97LwzcOqpwDffBM/jtw3WXRdYaqlkIwtkUT0+aYbAm+GxJb1uxc28Zxm0H3QQMGxY+T2KB5k8Geidw1tqSw35llXv8ZVkryfNkvZy2+OWGzSmWT0+yvrz0KY9zfWkOaxstZS0T5sGHHaYuY7aTUWCjqUFC4Avvih+ID5tGnDccWYEkyTs427BAmDEiGTLKQdL2nMvUtBu97Re6hVn5SKypIhsISJbOJPWdt53VFUFcC+Aq0XkIBHZFMAzAMYB6A4AqjoEwAcAHhORbUXk/wB0AfCSqmZUt61KBWV67QtJOUF70E3SXtcll5ih68qRpOS3c2dgiy2StSNOQ9KgHTD9AaSlb4KKMn4ZljQu6mmVtE+aBGyyiQley5WHjuiGDw/+zC/IHjPGpGnQoPjr8vst774LvPVW/GWVs84owqpJJ71uNTaapi5ffRU8T1a9C3vNnh382ZQpZnSRCd7uYWJYe21g++1btqpmFLXSEV3QcVTJ46tS1ePLXVap0s1Smfgo2yxqR3TlBu0t1f43zXbtaZa0l3tOnnoqsPfelb+22h0+BuV57TT897/ALrsARxxRmHbVVaZ25d57J0tD2IPnlhI3aGendC0uaoQzHcC0iK84tgHwo/MCgLud/2903ncG8ADMuOt9ACwJYF9VtceHPwbAUACfAHgPwFcATo+ZjuqXZvX4KP/HFZQ+7zK//Tb5OsLWE+ayy0yVyDvvTLbOQYNMlfhhw4qn9+pV3JNqkKCSCe8F0S9oT1OSbef3kCSNG05avcfbDyLKvfG3ZEd0SXgzu/a5VG7mCzCl9QccAPzjH9E66rKPp6g39zyVtH/7rTmv//a34Hny0sdE2Ll71FHAhRea/ZaU+1Bgt93Kv0anKU7Qft11phlQEpXOnEa53qWdka9U0F7pkvZSD+bTLGmPss3toN17nLTUNSEoaE9y3JZb0p5mR3Zdu5qS6yQFCkkFHT/2tnT7m3nvvcK0ckvGgwrLWlKU44XV4zMVNWjfDaba+e4AToZpw94ZwCHOqzNMr+4nx1m5qvZUVfF5neh8rqp6raqurKqLquqeqvqLZxlTVfVoVV1KVTuo6smqGlLs0AokOeHtE9EutUnrxu534qmaMWht5d7kymljnaQKMWAy9c8+C+y/f/H0N94wGeZS7B6rbX4lu9UQtKeRRvvmETQyQpTM2ejR5aUrLAOSRUl71DGBv/sO2GGHwvskmTfvQwC7KmDcdvJRt0uU+d56C/jkk+Jp3qA9qLQuzjFeqklFU1Nxk5K8Bu1udc20qrfbx1XWoj40+e034MYbTTMg93i45BJg110L9zXv9b8lM4tRAoW8Be0ffVT4P82Sdr9tYe+bluyILuj7U6cCDzxgmo3YQXuUe0RSkyebThL9Oigtt/d/W7kl7WnUfhk1ytR+dNkPJSpxXtrLDLqmlNoW5abLu92yCFZroXr87NnAgw+G9/VQxSJFOKr6ufsCcDyAi1T1ClV9y3ldAeBimB7kKWvlBu177mkCTu/0coIxvxOvRw/zspVbolHOhS7put3xmf060uvevfT3g56Se7d3Hkva/aoGxrlR//KLf7Vd+8Zw2WXNjxPvPEE3EjvgTztoz6KkPewYTfpZEO9vsTPjUZYX1h+ALU5gMmGCKTHec8/g7yXpPT6Jl14Czjuv8D4Pozm0NlEDBPshk/udO+8EPv/cPNS4/XZg8cWBTz8tLz3e2lZR5b2k3e/esM8+hf+97X3L4X1ocfPNZt+EpSXo+5UqaT/iCHPun3RS8bK89/I0rz233GJKnN0OSu19Vl9vmsEMHFj+esotaQ+rqfndd9GWcf75hQ4hk6YjDns/BVWPL7UvK1XbNS3jxgGnnFLowNBPLQTtF10EnHMO8H//V5n0ZCxJseQOMGO1e30PYNvykkOpKDdoB4Bjjmm+rKjLjTrkW//+zaeVe5Mrp6Rd1QzVdvTR6d5sR48GHnssWlV5m3efNDbmr92mX9Ae5ThpbAR+/x3YYIPiEQRc3hvDXnuFzxO0vyZNipcuv3T6/e9dv+uQQ0zbtiykHbR7v2Mfv1FK06IG7XFKM+zOHYMy1y0VtP/4Y/H7bt0qs54oWmsVwqhBuzewsr/Xrh1w+eXm/xNOKExPsk07dSq+5kQVJVBKcu1XDS6FjRO0lzqH0qza670eXHNNed/3U25Ju/tw5513iu+B3qA9zQDHWyPPXldDg+lEbdNNTV7D5d1vH37o/wDcVm719qBz8pprgO22i7aMWZ5RmCud7wn6zUHHUiWut968dtrrOOkkU7N1662D56mFoN3t0HrUqPTTkgNJIpzRAE7zmX6q8xllwT6w41ZPGziw+UVy4YXN30qWtPspN4Md1r6slNmzzVBtL76Y7hi9m28OnH46cMMNzT8LuwBWQ0l7qerxY8c2r96uam7ea64ZvNwoN4a4ndW1REk7YEpEKiVpYF5u20TVaEF7nz7Av/9tguuoQXvSEoasg/ZFFy1+/9132WUUGLTHC9rtPhmWWKLw/x9/lJ+mIUPifyfKtSzJ9eukk4BllvG/n8UJ2kudl2n2RF/qYWwaJe1hy4hTvXu55VqupN3LfhhTX18YWeTGG/3nnz4d2Hdf8wA8rOO6SpW033pr/GWVk444onS+XOnq8d7tlmR5U6aY+2+fPs0/GxBhgK+4bdqT7JehQ4Ezz6zZoLrS2iX4zoUAXheRv6MwrNq2ADoBOCythFFMQdV7SunRw78Uc6GFzF/7BD3pJFO6lKQzn6A27VGmJV1PY6MpRQlLg61SnUq51ec//LD5jSus12fvfqx0m/Ykwjqimz8fWH11839dHbDIIub/BQuadyyjWnq8bu88UW6oSc+L994zD1nC1tFST5yjZhySfhZ1vXa70qBM37ZOZSvvQ8CoaYuTzvnzgcUWM/97M9pBGf80g1tv0A6Ytq5hD6MqpbUG7VGHfLOPgYaG4utulIdLYdvXe89Ka2xs7/U+SWnj00+bvw8/3DyYixO0xwnqK90RXZwHCJXuiG7llcMfWMTpy+OII8y14+67o33HvgbbAXzQtdmuATJ/fqFgxi8tfv9HVYlq3pUuaQ86fuP0lJ5m0J70HDrnHNNs6+GHm6c3SvrilrQnOT522sk8XOjdu3lttTTUeI/2sUvaVfU9mAD9LQDLOq+3AazvfEZZsE/yOE+6n3vOf7pfSTvg3wlKFHV1wM8/F0+rRNAe1iFOqWWnNbxdHPZQI971VkNJe1j1eDtws4fT86uuGSWz4+2xPMrNw15unG23//6m5NR+Yl3JTobCRF1P2iXt3tLqXXctvC81xJB3iDnvtps2rfCkPU51zKAaRZUuaffbfn5Bu/sQoaW11qA9yoPWsWNN6ZOrvt6UOrqC7pdJt2mU4+2XX4rb2XuvZaqmw78jjyxMLydw8evsNE7QXuraWamSdr99euutJtOf9PtAedXj7f270krF8yetHv/996Z5zT33BM/jPR69Je1+/9vstIXtz3JLUisxDGOaHe35CUpzS5a0p9ERnV+T0zjiBu1J9q977vbrF23+uNuBQXtzqjpGVa9S1UOd11WqyqrxWUp6oww6wIOCdiDZhXzwYGDDDU3HP2HKDUrLaSZQbmajFL+MQliJZKWD9ro64J//LLyPcnH86KPiIZ+i9h7/++/mAdH8+f5t+73T/I7L8eOL31cyaPfjPReiLs/NhKex3qTV45Mc295SBvsBU5SOq8JKMJddFlhrLVMdOU4mMSg4qHTQ7ncddGuOlJqPKidKgPCvfxXfdxoaCrWf3GXYVeTLVep4+/JL05fHX/9amOa9ltXXN++0K+2gPU6gXerzSnZE5+eFF6J9v1TQrhp+Xffb5vbnCy+cTtCeZJx1b5v2Usuy77Fh+6jcBzCVKGm3g/Y0HlCOHVvIj3j7Ckpa0l6uNEraw463tEray32oExeD9iJl9NoFiMgAEVkjrcRQGdIugXWDdr/llhPc2iX7fifXOecU33SGDYv3lLWc8WKjVrUME/ei6S1pD8v8DBqU7ObumjPHDHPUq5d5f++9wKuvRv/+5Mmmx+Addihsq7De4+3tuf32wHHHmWFy/PanN2j32452ab13nqALdaWC9rFjgQMPjPadrbYywwImvZlkFbSHlTKUOg69x3rQ+fT99/Ge3Aftz0oH7X7Hjl/QnlXzFZa0B29771B3DQ3NS9r9rmNRxa0e794DBw8uTPNmhP2GH82ypD1O9fi77oqeLj/ltkmPcj1x8wlHHAGss07x9ikVlHjvX2kE7VFG0PD+5ijV4+3l2k1C7HNl+vTi4bHs9T/+eHHHdlFUoqTdzh+kcT3ffHOTH1l4YWCVVYpHsMmqTbvdn0bSkvaWCNrLrR4fV2u9twUoK2gHsBaAhVJIB5UraWYxSUl7OUF7lCro7pAUH39seuO1q+XG4U1nqZPfnr+hIdoFzDtP2H7wW7+3TXtYhqN79+IhpuK69VYzzNGOO5r3bsc1dvpUgy/EdumU29lSWPV4v23x0UfJg3Y7M/Lcc8Duu4fPD1QuaL/99vB53ePit9/M0DVff+2fcY4iarrDjld3GXFusmHHYpSgPepQbnEyAfb+TFLSnjQDEHUfVLq2TpC8ZmwqnalL0iysoaH4YWlQT81J0x6nGZbfuoKC9nKuXy1ZPf6550zNqqTsZV13XXnfD3qw26aNef/66yatn31W+KxU0OkdMzwsaI8aYJ51lv/6wwRViQ+61tq1+ux1LLOM6XvGHZnDe9yffnq09PitP63zP+3q8XbzikmTgIceKrxP2nt8qWtwWN6qoQG49trC+7Tz80BlgvaWqFnGkvYi5QbtlBdpZxbLCdrDTrIopYZuJ3iPP27+2tWxSymnmp59w91wQzP0m5c3zd6LcNhFzG+72O0avcvzS3+SkvYBA4BLLzWBo+uSS8xQdN707bMPsOWWzdetWuhkDCh0IFKqIzqvtm2TB+329447rvh9SwftpW4MdmdSLm+b/LTXG5ZBmj/fNC9YZRXgP/+Jtl57Xd59GeUaECXzDMSrThkUpGVR0u63DfJc0t6mjdk2118P9OwZfdmltt+oUf7XpaFDgeWXB267Lfq64ooStHvT39BQnN758/23X9J9Wep7SYP2ailpB5o3+4ojSqAXtaQ9KMBo06b4IfSSSxb+jxO0ewOxsKA96Dzyjq8e5+FT0Hr91ukXtI8cWZjmjovud+zFUYnq8Xb+oBIPKKNWjx89Onjo3lLpOvJI0yzGb/t68wZJS9pbImhP66FMmvvxxx+Byy4zD2Pt3zBiRHrryIlyg/YvAZR5hlMq0s4s+vUeH3Vd5VbTXaiMyhtxMiJe3vlfeqn4vd+TUu+2iFvS7g3a0+yF17XZZsAddxRn1O+8s3lHIE1NpnZD//7A//5XPETI7NnFVUrd6mRhbdr90t+unX/Q7p0WpVpiqfmBdIP2OO3b3HXZmWV7+8UR9SZZ6ry7915g4kT/3olLdQrpPTeiPDwKegDlrW3TkiXtSfkdO1Gn5UXbtuYacMMNwG67FY95HybsN33xhembYLfdmn/2n/+YwOiKKwrLOeIIcy1Ki52277836SmloaH5dcHv2mwfU6NHA3//e7QHb6XODb/7Ut6D9jgl7UDpc27qVDP82Isvll5WmFLXraBrkEhxHylBwb3fNd/7wCdsO0a5tsXJQ9iidD5ns4P2+fNNrbe11y5Mc8+Ba67x//64ccAbb8R7sNqSHdEtWGBqTnj7vokiypBvAwcCHTsCm24af/mAaYo4bJgZRaiUpOO0h52HlShp//bbeIVqtqi/L8p8W20FdO4MXH558W/YY49kacuxsoJ2Vd1PVVMY2JTKlrSkPWg88iyrx5cj7AZa6uQPy2y9/74Zk/W114LX5/feNn9+8+0dt6Q9TLlPLu0nyOefb4J9l/d3hbVpd9NdTkm737ERtn+y6IgujF8P+lFL2r0PQqIORVgqaA/qMdkdpuz884unh5UgRSlpDzoXw9p9Ji1p92YU4wTtUa5DeQ/a/fbt7NnA1VcX3rdrVxyol+oU1BX2m9z2rt6mNkDz7frWW+b6eeml0dZrGzfO//y3j4G6OmCXXZpn2r3pqK9v/vAnSkn7Bx+YpkWlhuR69NHwz2uxpD1uidv115vgxa82W9SS9mefNTU5vvoq+PtBnaq1aVNcymxvb7+gc8oUE7BdcEHzEu6obdo32MC/lNb7e5OUtL/5ZulleEvau3Qp/tw9BxZf3H99G24IHHoo8NRT4emqdEl7kC5dgMMPB7beOv7y7X1++eWFB+z2vnn9dfN32LDi706aZM6vqPmvhgbzAN3eZ979lUafSnPnmn12zDHmvXe43FtvNdc0W9yg/Y03TB9HYUMXBwkbwQEw2/Tqq4trxJTiNq11saTdEJFOInK6iFwtItfar7QTSAG8T5OTBMC//AJ88on/Z+V0RFdu9fhyHgqElVSX2kZh691vP3Px+Ne/iqfHeUr+ww/AaqsVV4XzZs7yErR7BVWPjls9vl27aL3Hxy1pb+mO6KIeS/bNLGpJu3ebRs0ElQra27Xz/+zhh01Jonc4R+/Y1rYobdqDgn7vd+NUt4vaEV1QrYhSJapBsgzaZ84EDjjABClB/H7XDTcA//1v4X3btsUPCYcMMZkub2eYd94JHHtsYRtGrXZeStKaJgMHmuvmdtuZjOYJJxTS5re9/yhRjuAtaT/11OjV4wcPNjUIwkqX3nknfEgyv+WW0xHdlCnm+PA+ULb3T0tXjy91bNhjhntFfQBw/PHmgeM//mHe3323eQhgpzXoftqmDXDGGYX39vb2ux49+igwZgxw333hJe1hQfvw4aYmm5d32wZdQ8KGfPPWCvQWBgDNg3bvdnbvO37H3o8/Fr5fqqQ4Tkm7qukfxj6n/UQJ2t94w/wNKogKY6fzs89M4O6mr5QVVwTWiNEfd+/ewIUXAgcfXJjmdwyUW9L+5psmj++OtGAv76uvgCuvNLWHbHGDdlecwNpV6vd5719RqNZ8m/aAXFwwETkNwEMAJgMYD8DeQgrgxnSSRoFGjAB22sl0SnbZZckzimE9h1eqpN3+bqlAK8lFK271+FLDm6hGfwjh997Pyy8Df/mL+b/SJe1RbnYuv5u1+/uDqkf77cOw6vFptWn3aomS9ijHrndddtCeRkl71KF6vMKCdr9ljhhRXE2yUkG7N8AutdxKtGmPclxEDdAr0RHdXXcB775rXscdF/17bhtVV7t2xRl3d//utZepKuu65BLz9+ijzcNKv985b54p0XI7pPQTdQSBUp5/3vzt16/QpOeMM0ypd1ATnLB0eIP2efPid/xW6sHApEmmZpafNEva77oLeOQR4NdfzfERVDW8VNCepB2+LazZmN+2itomPQr3oZPbV8eYMf7Lsve5SCGPA5QuabfFCdq91x6/B1dx+wsJWpdt7lygQ4fiafa5P3asOV5sw4aZQhy/oHerrQr/l2q+GPUh82+/mdoL7v46+GBzLVpyyea1Zex8UtCxU077au/x3bdvvGVOmxY9n2j3JTR3rmku5OYH/eaPI6gPA6A4ffbxbj9gjNum3VWJh9Vuv0m2SZOApZYCFl3U/zutIGhPUtJ+NYCrVHVlVd1CVbe0XluV/DaV75przIXVfRqY9hjtQCHjk/YJGqV6fFol7VGWU2r+uJmZKNvGzlSGBe1xL9x+N4047bvCMopBGYuw4yOopD2N3uO9ogTtd94Z/P0o4gw74xe0Ry1pDAt2wo6vUr3HBw1r5fe9XXYpLr1JkrGMErTPn1883557hi+zEm3ao7YxTDqtXKNGFf4PKqH0O/ftoAQw+9+vgzC/0j+gUHrit6/feCM8YPdLUxoZUS+/7V1qCDZv0B5n2VG5+2nUqOKeyYOWm7T3+IsvNgG7H/s3llvSXuphWtDoEp98Aqy6avP5yw3a7e97t4v9cNRelr1Nm5qKf1PPnqa2ws8/+wft9rUzTtDuTdvxxzfft1FL2m2q4Q+x3ftOUBDnNyzfqacCN91U+uFy0qDdb5/bD1gOPdQ86Orbt3g4RMC/5oBXOcGa9/hdbLHmyyynJqP3mHFdf715HX548fxp9B4fVmXd3of2ULpJS9rjFA65Sm1Pv1qcK65Yuk8BBu3NLAMgpIiWKs57galERtE98INK2gcPNlWC/ve/eMuNUz0+6kXy99+BG280GaWwG2ipKpB+GRN3mt+YzADw6afF76NkTO0LZpod0UXp6C5Mjx7Np7m/P6hNs9/vbWw0HZ75bc9yStqvuqq4HWKp+e10AoXqc6UEDc0Wp1ddd732U+ykJe1RM9dxqseXOg+9IwtUqqTd2ya01LB4lShpt6spRllvnGnffmuqdnurr/oZNcr0LeDtqM1eplsV2CtK0O4taS/F3b5+vzNJG8aofTPY82+/vf/wiu6x7Je2Up1WRQ3ay3l47HbUudZaZmhKuzp90qA9bq/e9nr8zitvra6w86TUNg1qlx23eqvfssr5vv2/few3Nhbv3yeeMCXPBx3kf5za186w61e3bkDXroX3fseQ9x4Wtb8Q+xxvbAzfJ27tA3uf2nmBJKPQuEoF7eV0ROe29/aKMlxq1OMmrHagyw3aSw3zZgsbzSbo/meP6GPz9h4f9bfZ89nXaO/yog6hN22ayVsPHx6eliQjDpRq0x5UyDBsmMkLBklrmMGcShK0vwpg77QTQjF4D+ZKlLS7F7GgoP3KK81T0rPPjrfcoIy2d566uuKM7oABJnPdtWvxBQQwpXPXXWfaYdrLnDevdIa9VEBUKmj3tnEvN2hvajKlJzfd1PJBu59DDzU9/AZVj/f7vT16ACutVDz2rKucjuimTgX2Drj0RAnao1iwwKTdT5ynyW4m+LLLCtOi3tiSVo+PE7RHGS7PVm7QHtQRXUNDtPWPG2eORbtUPKxNe9Rx2qNkBr3rijJN1ZQy7r67SftRRxWmf/aZCep+/tlc0/r2NQ93Nt/cPID0dtRml6736uWfvihB+2KLlQ62/faZ3++M0vN8WEl7lKChd2/z8uMev0FB+5QppgaBX3XJNIL2Uhn4iROL39sd9SXtPX6XXeIFQd6mQaWacoVdW+KWtLvrTjI8VqWGfLODdm9Ju+uXX/zHPLfzXPbn3pL2l182JdY//1z4vJRSJe39+jWv1TJ/fvj9xH1AHPRAtJzSyDRL2v34tY9Os6TdL/8RpaQ9bH6g+Fj37kN7H9vHYdDyvW3ao+ZjgprE1NcXLy/KEIGAyb9cd11x535+vz1JPtNOzz33NP88LKi/5Rb/6TVeyg4kaNMOYBiAm0RkewADABQdTap6v++3KB0PP9y8t9QkJe2zZ0freCyo3WbYCRWWsQgqKfMu/+GHi6fttpvJiLk9bjY1FU56t3rgRx8VB9Fz55bOAJRKT6mgPWx5QezgyXvjHTGiUHXtnXeirdPld1Msd8zVDz80L+/wcO628tu+N99s/tod7rnatCndEV3YhTeoKmjUphaNjcFPcAHzICpKSXuU6vHedU+YYKaVyvSElbSX06bdzYgA5jyZNMkMkZVkGKu0qsdHDdrPOad5TYmwTumijlJx7LGl1w2YjNZtt5kge8MNm6/fTseQIabtudsu0vbee6Ya7uKLA6usYh5Avv8+sO66wfshag0NL2/QvvDCpUvavQGJ/dcWVtIBmDR796s3aA9ql+gKO9anTjXXDL95GhpMgDtokClB9erd27QDLyUsUJ0xI7y/E2/QHlSd211GlI7ogOCOY/14t83MmcAyyxTe+wXaftemJ580w4CGCSpZ9AZI7nYIC+Ci3EPDHjgEBe12h4uNjcH71972d95pfvsBBxSmeYdO88vvTJhgeotPErS7NQbbtjVNqrbc0kz/978L8yxYEJ5/c68Z3t7EXWkH7dOmAe3bmzSXU9IO+Dcjs9P+1Vemp/izz05WGh2lvwi/knZbqYKHBQuKr7/2sWY/8Ax7KOC9ZkTJgwZVj6+vL85XRA3a+/Qxf4OanLiS5DPt33fRRc0/D8unBWGbdl+nA5gNYBcA5wC40HpdkFrKqLkvvjAXbruNIxA/aK+rM0OlBD2tAkzmoF8//3FvFywAll228N57wQu7eEYJQObPb955jbc33rFjSy8/StBebkl72PKC2Bcr7xNK+8Ift0dOv5tpuUG7y69N88SJ0cZF9n6v1A0vSfWmqCXtpUrLveeWLU7Q/swzzdf1yCPAIYeEfw9oPjSL95yZNcucn3F6bPa2HX/7bVNlt3v34l7jg5aRpKQ9qGQ1rHppEL+hW8Kqx0fNoHbvXnrdgBl65oorTGm4K+iB5sYb+wfsgAnaAZM+t8ZQQ0N4+3D7ONpkE/95/B6ieq9ZjY2lg3a/tpdx7y9jxwJLL134rS57OVGrZwY5+GDTlMDvml1fbwJ2wH94qigBOxB+jJ94ojnHAf9rj/dBS1DQfsop5vveQDPoOuVtthLGu228aQpqh24bPBg4+eTgdbjpDqoe7/0d7dqZh8DPPRe8zCgP0oJqfnnTYv/GKCXtQPN75kcfFXfOZgeVQdcvvzHd/dIKNP8tu+0G7L+/6ajtb3/zT1fUoN2bH0qDN58xYoTJD7oPNsod8s3vIaWd9mnTgHPPbd4XSdRgLUrQ7gaMQbW0SuVhwo5P+4FeWEm7LUlJuzdoj1I93puejh2bf+Z3vUtybLn3rKDfVqr6vB8G7c2p6tohr3UqkUhy/Pab//S4VYCHDYuWadpySzPEid/62rcvvHfb77lKlfj5/W97+mlT+hTGr2dJoPlNqtRNw744+lUddS+23lKrKMsLUl9fuPGHBe1RqqDavJn0448H9tkn3jKC+AXtF1wQfzkNDcE3vFmzTMmGt/mDl99+ihq0e7f33Lmm6pfb83DYuJ5xqsfffLP//O++C6y/fvMxcm3e9pPe6vGHHmqahNx1l+mMcqutTO2AUkG73/nmzficfbZpR+z3fZtfZtSbwbH3yT/+YY5H73fr66ONU+xXMmefH96MYjlVQf1KgN3OxBoaTBV2oLBfVlwR2Gij4mlpss+XoMyRX4mT95q1YEHz4d3C1uVeo+LeX155pfk01eLzIUr1+FL7beJE/+1hL3vmzOSZuFK/+7rrzF+/a493O9sBo52+J580NarsZUybFlzbJ2woOa+wDtqA4g6oAP/rVanhs4L6Njn0UP9lqgL77hu+zCgWLCgOHoOC9pEjTc2YBx5oPuRZ0P712w72/rC3Y1BJu3vMRRne1O9c+PBD4LDDimur2Q+gSgXtM2eaWib2WOBpVY9/+GHzIGeXXUw6nnzSTHfH/PZei+PWFCpV0u4aOrT4fVi+c84cc78cPtx/Wd5zxQ3sg0bziVs93t7H9jkclOagDo5Vw7dnUM0K7zEWdOx4j4vVVy/8v8ce5q/fb09SODR7thnhJOielCRoBxi0U474ZVxVg9s5Auai7y09DBr6Kapu3YrboEyebE7Anj2blxp4+VW/9HrzTdOGPUxQiai3TXtY0N7YCJx+euF9WAYwzZL2Z5811RTvvbf5Ou2ANO54o97SmrBxnePyC9pKBdd+GhqCMzKXXmqGm9p22/Bl2FU8Xe5vr683GYrXXjOBsbdGhvfm8uabwA8/mDF+geLebL3ilLQDwTfGX381JQVBvEMReUva3Q4DH3jAdNL144+m5CpJ0O69MT70kH87Yu9N3+/BiXdIJe914NlnzTylqsf7BSx+177TTzdtSE86qbjasLekPU7na4AZcijM7rubv25G6uyzTVVYe1qQJD0Q28fRiBH+zU5sdgbPNmpUcdtqP/b1+frrTSnjP/8ZLZ2lOi+N2xFWlGupX4bP/g1JmxYApdPojs0cFLTb58O11xZqtnnTNH168TIOOSS4Lb8dhJXiPd/d69dHH5kaG94O0ebNMzVwTjihcA6Wyji7x6bfg7e6uubXwFL7NGrJ7A8/BFc/ts/Ba6817cvPO6/4WHGr/frxC0Ds0tFyg3bvQ+ugfJC3SZotSkn7KacUT7MLOsoJbObMMYH6F1+Y49S7LHv7H3+8qXXTt2/0a1/UoL3UKBG2a681fQVtuWW0kRnc9dnHdamCLrtQLWpJeVAfI2PH+vcvcsIJJv/z0UdmSE7vdcLeBvbv9Ja0B63Xuw3t73z2mdn3UUraGxqAzz8vfQ098sjga3RY9fiwId9qXKToTUTuBnCNqs5x/g+kqj6NEygVfjfQxx4zwXIQd3iESy8t9MKbZHgG2wMPFL+fN890vvLyyyYQDbvx2jfOckqlgjLicUrau3cH3norfD333Wd6yI/61G+LLUrP41bdvPDC5p+VE7RPmwacf75Jc1pV4Vx+VfqSXCAbGpq39wTMMemOFV0qox3W+/Jjj5kMhfv03+uLL4qrX9oZv7lzwzvqSitotz9fdFGz3kUXLRxj9rE2e3ZwkxK7g7K6uvCHZQsW+N9AH3ssPI0u73nkV+vHu3y/9EyeXDponzXLZPRsQRk+b0eQblrtwN9ujxol47jkkuG1XNyHVe6+aNcuvDdzW7lBO2Cu6VOmFJoozZ1bXG16/nxzXPvt71IlIt6MaZyaOm7pZ1DncPa6owTtUe5TfiXP9m8op6S9VBp//dXc7/yuGTNnNk//VVeZDly9Jdx+D7i8TQtcpR662IKC9qB9Wldnek8HgPXWM0PLljpeZ8ww44H73WcnT47Wwajdz0jUmgQvvlj83n1oBgQHV1Ef3vmdI3ZtQm/Q7rfc+nr/zv/s9I0YYarBr7tutHTZolaPD/t+Gry/sb7ev0r+bbdFX6bf+VRq9AO/9zY3jzxrln/eyHvMuPPY+7ZU0G4fN9OnF3do6w7P7BV0vPfr598BrVsQ457DL74Y3OmqnZ4NNij+rFs3//V6z0/vb543L1qb9gsvNPnmc88tbn7n9ccfwcdqWJ67rs4cE0ssUTyd1eP/tCWAhaz/g15bpJw+svkdxGecEa2qe+fOhf+9Y2CWa948E7ADpsp0WGml3U67nCF1unY1v92bsYoTtAeNeWx75BHTC3G5bTCjsm9YbinxUktF/757gYzaK3ZUftXjk1wcP/nEf0iXurpkHY+4vvzS7CNvUw2v444r/K9qMqaucePCgxo3k9TYCDz+eOk0lQo6fvnFZOCXWaZQevvYY8UPa7baKrizRPuYHDy4+Lf5paWc883Lr0PAUiXtgDnn7HT7tQn1y7TFqSrn7aAsbjOTDh2izecXtIdt419/bd5XRxhVUzo0fnzzz+zl3HBD8Wfz55vmRfbQU1FFCab9aroAhf0aFKjYmWXv9XT27Oaln1GCdr823kFVUeMqtS0mTDCZ0x9+aP7ZjBnBJXrevkqCzpUkgqrzAqUf5NrpdY+vUsHdu++a/e2X/jXWaP6Awo+9neOcHza7dDbouCnVNCTs+0FB+7x5/gFQXR3w/ff+y3eP/UsvNX1ZxO1wFigdtIeV0gPpPdS/6qriMd8//dT/mAnrtDEKv/R6jzlv8Hr55SZwnDq1UFAC+J+X3m05b57Jq9gdbsYp7Npww+LOlIP2sff8cO8jH31UfMxFadoEFG+TsEKGoL6IvM32/B5m+J3r55xj8quTJ5v+PtyhoB94APjuu+C84uzZ/kH7N9+UPl6WXLJ5fo9Bu6Gqu6nqdOv/oNfuFU0t+XMDtJVXDp9P1Zz8xxyT7vrjtGeZOdNkQh94wD8zGtXw4cCjj/p3tuTyBu3ei0DUk3vIkPJrJ0RlByzu0+Hll4+3DG/14DT4PXFNso6g78yaFdwzfBSDB5vSarfn+ij69CnuAGzs2PDf5B7npWpnuEp1JDhjhrmZu1XJgOLmGkChJ2FXUCb60UeLx4L2mjgxnaDdPd+GDfMfSsvmF7xNmlQ835w5zTsLc8+B0aNNJmLy5HgZvrASNdXS16tygvawIGf99YHXX4+2bFXTDMLOENvuvrsQqHj7/6irA/bbL9p6XC++aDJKUR5Orr22//Szzwb+8hfzkNOrVEn7xRebZjF2b+9Jr7lpPWAtZyzr3r1NrScvvwfF3k4byxHW/KzUAwx7/7jVT0vtg7POMg9hy3no4G7nurr4tcv8BJ3fUfMbfg/5goL2IAMHAjvs4P+Zu4/iPky0uc0PgpQaZSCth/pff118nuy3n3/TxnIDKb9jN6ykvVcvU7P07LNN7Uf7vIgStM+dC5x2WvG0uPdPu7f/IN77xVprFf63m4DOmxecb//9d3Of9I7YkSR/5tbGBczDSG8TyzlzgvuTOuMM0zfQ008XT99uu0LzQ6+mJv/z6f/+r3jI5yBB/afUMLZpryZBmRH35CwV3M2eHf1pcxxxO6HYe2/Txuzdd9NPi/eiFTT8SxzTpmUTtLtWWCH+Mrw35c02C57/wANLL9MbCH32Wbo1NqJW006De1H3ZuJGjw4/lt3PoraTLZUpmz072pA1331X+D9ptcZRo4LbycbhZirmzWu+HbydTPoFz/37F2fy/K4Bp51mqo526mSq162wQvE2KCXsGjdmjKlW7u3EyGZ3shnG/b3t2hU6xUqr2ml9fXiP8k88YUq8/QKRUtWA/R7uHn20yShFCXjtkUNszzxjSrTcB1C2+vridD30UHHbfLdHd7sdbtJrblojZpQTtAP+mU6/mkALFqRX0u7uv19/bR4An3CCeTgSxM6w33OPSVeUbXnXXeU9dGhoMNfCjh3jP2zyE3TcDBsW7ft+/eXYJaJRrv9hQbNdYyupqPsmSKmArpx+j778svm0ckva/QLmyy83HZldeqlpDmcHa2edVfjfWxvH7/ro3ZaTJqVf8BHFqqv6T58yJbi0/rrrzH1yhx2CS9qTsMdmd82dG3wN6d8/+J7lfsfvmC+n3xGgeL/bNSr8Pq8BDNqrSdDNyG0jXCpov/328k8QP34Xh5NOCp6/VCdz5fB2RGdX1W9sNIGUexJHPZn/+CPboD1uSfsllzQP2jt1Cp5/l11KLzNuZ162Qw8FXngh+ffT5h5/3pvyzz+H36jdcyfqSAJRgnZbUEnYpZcW/k8aFH73nRnftlzt2xdKov/4w9R42Wor0xGeHeS4owF4XXRRcemxX8njTz8B66yTvMS01LFaVxdedTxJSbvbtCOo7WJc8+ZFKxm87rrm17GwfhmWXrrQ+72fKNe5oKA9jPd4eOIJ0za/bdvm1wb32pX0mpvWPS5J0O5tY+nlN1Rpqf4o4pg1y9S4WX/94r4cXEE1NwDTCZ3thRei7YNSnc+W0tBggtwozdXKEdZhr83bQZ9XlCr8Yf0Mude1crZZqerxpZQqaff2KRKHX6e9lQqcttgCuOMO0/ms/RDwp5+Cv3Pkkc2nebflxImlm9pFFeeeHdQUcvLk4MIbdwSYIUOK92vSPFtYsB9272jbNvzBTJcu/oUzSZvEuOwHOgsWNM83pN1UNGMM2qtJ0Mn088/mb6kS2f/+F3j11XTTBPgHOlFLq9LmDs0EmHTtvXfhfa9eZhudd555HzVT5he02x3fpMnbyR/gv1+DnsgC5sLozbivuGLw/IstVjpd5QTtCy0ELLdc8u8D0R4sROWOtX3jjcXTb77Z/0mtq1s3c+OJWlulVCbUW9Lul6H3mjOn9EODuDUz4mjXrlBSO368afP344+mPf3VVxfmC3tgYbcBLue4ChI2nJ5r+eWDS7oWXzzaeuygPSxQTqKuLtrx8OijzXuT9watd95Z+P+TT8z5GHQMRSmNTHIub7KJ//nQ1NS82qfbtj1pSVGpZilRJcnsrbJK+Od+bfCDOndKomPH4lLGUk44IThQGDs22j4IG/M8ipbqLyaqUsFa0Mg1Ubm/t5ygu9ygvdR1N04/Ol5+/Rg0NZVX0l7Nwh4geNnDGNrGjQvOTwTdr5LeWxdf3L+JExD+4L9Un0Tnntu8/xWgvFqWjY2lrz1B7ferFIP2alLqIh2lRPayy9JJi83vJpfkov9//1d+WmxTp/qXXnbpYqrgRu0Apn//5k9Lyx02zyvshua3Xw8/vPC/G4TavB3RhPV3ECVIcQPVoMx+WOmkSOkSqFKChvgoxX1A4zVokH9Vrl9+CV/euedGa6sGFHdy5+fkk4t7iY7SnnPQoNI3qQ03LL2cqDp2LH7frl0hMLnrruIAye7R2W+EgDx56CHT3tFPlA4RRQrDb7Vrl16g6Kqrax5ARz0HvIGhfX6752nQsrztOP0kfQBXaqg61403mhKdpEF7WiVkpTrz8uufoFRP4H7XjrCgPcoDKK+gNqd+nnoq+Hpy5ZXAmWeWXsakSaY5S1IffxxvKLuslfugsb7elA6WKtEP463ZlLZygna/Idveequy6c2rIUOAbbaJPn9QvrJv3/jrLudBsjuKhFdYO/M2bUo/mPGrPZZk6GCXd2hNP/vv37LNLyuMQXs18Qbt3tLWuNWoS/nrX6PN5xecxL3oH3VUtOHS4ggrNXr88dKdtbi8VYK6di0/CPUKGxvab7/aJTp+AfmDDxa/d8cU9rPOOuFpAwpjDAf97tdeC243P2xY+TUTkgbtnTr5t8GyR1OotLCScTvQPfHE0stya9WEWW210vNE5W2fvNBChevOu+8WhunzynsGbdSoQjtqoPjhTtD+CspQRQnaH3zQv62nl3vdnDev+fVr0KBoVdOPP774vX3uuNeZpOcTkKx6fByffQasuaYZWzmJOLXJDjoIuPVW/89KtWn12w5J7sH33NO8iUD37qZmWNCDpTS4meWsS7rPPDN4iM60hTXbiytpYHvDDeYaU87IBvfck/y7UURtAmY74YT001Ht/DpKC6Pqf4yGnR/lBL22Lbcs/B9UQBhWsBAlaE/bjBnR8hpxRp/Judr5Ja2B90S65JLias+lqsUGtVMKKoV9/vnISWsmyQ0tqGpQUn69kW+ySfLlbbmlqTJ48snRqpT7CapNEFYV8y9/aT7N3md+JYPeko+wQG7TTQv/Bw3n5PJrr+ZOt4ccsx/A/Pijf2b2xReb904ad72lrLKKCdC97aaeeSbZ8pKIWisjbpvOoIxVWiWNgDmP7eCkXbv0H66V4tcLd9rsB48LLQQce2zzeeyefW0LLVQ6aD/lFGD77cPnESk0Kxo/vnlthXXWSfbwy64l5N4DSrUxFTHXDL8MZNQM/ZprAldcEW3erOy+e/B+LcXvQWip66cfv/P+H/8ofbyUo23b4FEA8qic4UDte2WpEXbi2HHHZN/zK4lOqpy254A5zjbeuPn0JEFO0vMob8KaHsYVt7PeBQuiDSdbCaWa9pTy88/hI9hUwgMPROtlvpqudSXUTNAuImeLyEgRqROR3iKybdZpSp1fSbs9xNX66wd/d/31g9uZdu/uH2Svtx6w007JnromGV4qanWxc88tjAsfV5yqSgccAKy+euF9x46FC3rSTMTdd5vM7AorFLfl9FaPtKs477RT8+XY1dGjpCUsaLdv/I8+Gr6coHbADQ3FgfmVVxa2tZu52Xff4u+0bx+9qm3SksE11zQByMorh1/cvRmOpZcu7g/h6KOTrR+oTC+0m2xSHDRss00hvRde2Hz+sI6RwiyySPGx07atGcIlCW/VYfua0769/3jXgClVKlWa4NYEccWtrut9COdXwvmPfxT+tzM47duHB+2vvGK2Y6mHN+3aFR7C7rGH/zx+nfbcfrsJwv06WALMsf/99+bhmfvwq9Q1Y599TCeedjMcV9TMXZ8+Zr9EGZ0iLXGbhsyfn7wWkF+QEqUWQtyaCu+/3/xeFEVYk6UllkhWKuZtLlPK/vv737/iGjCgeUd5Udnn7UorlZ8WVxq/K2qnl0Gi1N5x7babydPZtt7aPy/Vtm1xU7HNNjPDebm6dTPXHPthejkPVtIStXZomE8/Le/7XbsWrg1xS9pnzmz+wMTOV6Xdl5Kd/0r6AMg7TG0cdkGPze9hr1/HmvPnAxdcUHo9NRS0Q1Wr/gXgSAD1AE4CsDGARwFMA7BixO+3B6AzZszQXDOXycLr5ZdVP/qo8P6PP5rP47723NMsY+mlm382f77qrFmqkyerfv11Ybqq6pw5qlOnqj73nOp556mefHLwOuzXDTeEf77zzqq77VZ4f+65qquvHm3Z++2n2tTk/9mhh6qutppqhw7+n990k+rZZ/t/duaZhf8POcT8/uOPL0x77LHCvjjttGhp/fRT1S23LN7WM2eqzpih+uSThennnFP8vS5dVFdYQfWVV1Tr64s/mzXLfH/hhc37KGmZNq35tG22Ud1/f/N7Pv1U9b//VW1sDF/Okkv6T3//fdVXXy28HzlSdfx41SuuUP3tN7OOhgazfwDVVVdVnTev+Pj97DPV5ZbzX/4ZZ0Tb3nY6777bHCe2/ff3n//mm4vf//ij6pVXFt7//nvz7zz8sP+yvOdY27bx0l7qtckm5hjaeefCtAULzG8dM8b87dmz+DtjxiRb1/z5qsceW3i/6KLNz4uw/XPRRarHHad6zDGq48apPvts4bNTTy38P3y4We6//lX8/TffLOy7nj3N8XHttc3Xo6ravbvqKquo9ujhf70Meu2+e/H15D//Mcfq1lsXzzd1quq++5rz5rrritP+l7/4L/umm4qPwVJp2WOP4M9UVRdZpPB+001VTzqpsOxTTmn+Hfec9go6D9zXnXea+Z57rvlngwapTplSPG2vvZrPt2CBWcbuuxdPX2ONwv8bbRTveFxhBdUXXiie9sknhf+feqrw/3rrqV5zTfG0rl1VO3YsvH/oIZPGLl1Uu3UrTPceh97Xjjv678977jHrCPreMsuofvON6iWXhC/fj/c+AKjef3/wMhoaVPv18983Sy7pf0zus094uurqVB99VPWII6LtL1XV6dPNfcC738Je9jECqM6ebZa1zTbxjpeTT1a98cbC+0cfDZ63Y0fVNdc0/3/wQellf/VVvLT4vd58s/m088+P9t199mm+/269VfX551V32aX5/JddpvqPfxRPO/NMc33YdVfV7bcvTD/ySLPssWNVr7rK3P/efrv58emm9eijVQcPLn97uK+NNzZ5iAkTCve5/fbzn3fDDc3fDh1MmjbeuPk8m2yi+tprpde70krR7x333KO6ww4mb+3dB2edlex3H3hg8frXWkv1mWcK773XePe3B70WXzz88+eeM/eyvn1V33jD3N/95ltvPf/pV15p8ncHHhj9N7ZpU/h//PjC///4h9me55+vOmlS8+8tWKA6erSJUfbeO952bWjwv6bmyIwZMxSAAmivGhKvhn1YLS8AvQF0sd63ATAWwOURv5//oP2995ofiO+8YzLvgMnM2BnPjTYqDrBGjDDLGThQtX374uV4ff65mS/I66+bi+CyywafJIMG+U/fe2/Vt94qLOvll00GcsqU4swVoHrLLeZGsdZaqostVpi+7bbmu94b+3/+Y6Y3NakOGaL6t7+ZC4AdfL36qn/mBzBB5NNPm+B8zhyzrB49zGfuTcw1YYK5ab70UvjFQtWs75BDVO+9t3gZn35aPJ+bobjqqsLvcP+68/XuXfj+kCEmrZMmFR5SHHxwcDq827epqXlQq6p64YXBv8cOFO3Xm28WtpW77CD2ej//vPCdBQuKgzr79cADZv+67484QvXjj1XffddkTL/8sng7XXON/7rth1KAyUR362Yu6kOGFKZ/+60JuNz3c+YUf2/KFLM8v5tmU5Pq1VcX3n/9tQk41147/FhxXxddVLxdvK8TTzTrdoOVf/7T/7e6gdmqq5r37vfXW888MJo8WXWJJYLX8/33zfeReywtWFB4366dmXbppeb9csuZG3OfPv77/pprzPkwcGDxuadafDN+4AH/76uac+ebb1T//neTqfETFtC4ryuvLCzzr3810378sbCuW24p/t3usfvgg4XpjY3mt9gZmz32UP3f/5qnqVR67IdYiy9u9h1gHkSqmiDTTbfXxRcXL2unnfy3i6p5gHLAASajtvLKxcfx1lsXton7sGfddVV//tmcc66//c18tvHG5qGgfV/ZcsvCfFtsUZwuO+CbOtUc74DqI48UZ8xHjzbBsf1dN1M7bZq5D7nHzcorm8/HjDEBIqD64Yfms8ZG8zAHMPfLpiazroMPLnzf9cQTZh2zZ5vlXHqpmcdOw+uvm3Srmuv/vvsWrr/ufdYOFIOuiXbg7g0ogniX2dCgOmqUuU++8YZqp05mun3s/fqrOa/shxJrrln43E3rrbeq9uoVfGzuskvhO9Onm2kdOqgedZS5Bjz4YPPMtM3Nq9iBxGWXNb8mA+bcfv1187DvsssKy/BuJ/cY9L7uuquwvd2809/+Zq5J3nn79TMBuPd+uOWW5v7gfUDpvurrzbUYMA8m3ekHH2zyGc8/bx6YuIURO+xg9ou9jPnzTV6tQwdzvX35ZTPtscfM57vtZq4l3bubbbzyyiZ/8M9/FgIRd1n77VdI+8svN0/vM8+odu5cPO222wrfaWoyebN99jHBuldTk8lzjhpVmDZ1qrn3uw/o3OPC+3rqKRMYfvFF4T4BFD/sPOOMQqGQ/bDW3S/utQ9Q/eGHwv9vv2328a+/mvnXXbf5+mfN8i+48L4WXtgsY6utiqcfdljxe/eBiZs2+7PBg812ct8vv3zh/x9/NPc1d/8C5hj79ltzL/vuO7PcY44xn33xhXko7M47ZozqQQepXn65yW9Nnlz4bJFFmqfzmGMKD02XW0718ceLPx83rngfu/vx+eeL57O3t9/57RbQAOY4festE3+0a2ce+HTv7r8t7W3XtWtxWn77LfhaolqcxwKaX3NPOcWcu9df3/y7OdRqgnYACwNYAOBgz/SnAbwZ8J1FnEDdfa2W+6C9ocE8vVtmGRP82SUokyaZC5KqCVLXXFN12DDzuvji5iemqglC1lvP3HCT6tXLBO477GBuOp98Yp7E//67+fy110x6//53c7P59tvw5TU1mTTPn18onVU1pcp//GEyVB06mCDC/Q1jx5r1/+1vwYHi1Knmxvn3v5v0qZqb6vLLm9LSjh3NRTDIb78VLmZ+pk0z2+LMM82TylNPNReUwYNL/97bbjMXePv3+5WMHXCA6mabmW3j5+efTeZm4kTVCy4ovgm99lpxWi+/XHXAgPB0DRtmvnfIIebiu9RSpgT5xRfNvnBLPo46ymQU584137vxRnMDjaqx0Xzn/fcL6/71V1OKeNBBJhi/9tpCBqWxsfjY8OrTxwQ07kMXP2PHmgzucssVZw5UTYZro43McTJ5sgmWzjnHfOZmSHfeuXCs9eljzoEuXVQ337yQcZoxw+wPNwBUNTffNdYw2/+338w6zj7bbNtXXzVp/v77wrH2009m3iefNOvYZhuTsbF//8iRwcfElCkmgzRkiHl/443mNw8dWphn6FCTzjffNNvts8/MOfvEE8XLeukl8+DMDgbefdcszw0gm5pMhm7ePP9rjp+HHjIZOtc335jj7PLL/c+DJKZONelZbz1TgtqjhwnGhw8v3nZ1dSZQtE2YYAK+f/+7ePqUKWZf2iXdqibj0a1bcFrsjEW7dubh5v/+Z65rTz9t5hk50mRwJ040AcVllxW2Z1OTSbffthkxQnX99QsZQfvYCzNlisls9+5ttov3eBo71v98+vFHc27OnGneT5um+ssv5jVrVmE++6HPffeZ9C+3nCnhUTXH+7Bhhflnzy4EDUOHmnWcdZZ5MG0HDLY5c8x1SdWk37sfp0wx53NSdXXmmv7NN8Hrt4/58ePNPW/HHc2+uOOO5t+ZPNmcn+4D8rvvNt+xH8x6vfWWuWdMmVK459saG831M+he2KWL2fa9ehWmNTWZa4r7nYEDzQPSH34w06+4whyH3mNg4kRznaurM8GE69lnzQMc+wGPa8IE850vvzQP7+rrzfRhw8w97rTTVPv3D/79quY4f+opc+2YMMGc1xtsUDgWhw1r/vvte3jnzmY7rLhi8MNdVbOsSZPM97wlp/36mXkWLCjkd9y0ec/NGTPMetx77pQpJj9yyimFzydObL7+338vPhfHjDH3Wa/bbzd5Gfu6PmeOuS8fdZS53t1wg0mXe/347rvS98mknn3WPOBZaCGzrU4/vfjz2bPNA+zjjjPXkGWXLTygXbAg+P7unlPnnWfev/iiKejw6tHDLPP118155+ZHmpoKNUmWWMKkcZFFzMPayy8312D32j10qLn3d+5cOJ/uvtvkG0aNal5y27u3OWfc69jcueZh5a67Fh4Aes+5m24yafRTV1d8PXnySVPrMmi7zJhh5p83r1CbcbXVzLasry9cNxsbCw9K9t3Xf3mun34y2/HMM837Bx4oPgfsBz4ud32q5txxH26qmjzenXea37bppoUHsKedZs4He17X11+b/IBbI8pryhRzvXK/e+65psDA73qbc1GDdlETxFYtEVkVplR9R1XtZU3vDGAXVW3WAFNErgdwnXf6jBkz0D6r8cWjUi3dFi3KPHHmi7KMoGWV+jzp+rzTgPDlh60/rbSlvSzvcoFsxzottY/TXheQze+1f4/3/7o6077eTpd9jAPx05zmOV3JdQWde5XY95U6h5IsN+3j3u84SXP/uvI2LnLQeZXk+9Ugyb2v2n5jkGr4HXHTmOY9Ke3t01LX5riiXDuzOD/8zs08bK+0lPotSWOEtO4vfstNK38+f376nVq3gJkzZ6KD6eOig6rODJqvZjqii+lWAB2sV8weXjIU5eCNeoCncYFylxG0rFKfJ12fd1qp5Yd9nuaFulIX/Si/sdJK7eO015XV77XX6/1/scWap8s+xpOkOc1zupLrCjr30lbJcyjN75WzvKBjqFzusrO+VvgJOq+SfL8aJLn3VdtvDFINvyNuGtM8r9LePi11bY4ryrUzi/PD79zMw/ZKSzn54bD50rq/xLn/xV1XFQbscUQciyjXJgNoBODtFnQlAOP9vqCq9TAd1wEApJZOViIiIiIiIqoZVV/SrqoNAPoC+HOMHBFp47zvFfQ9IiIiIiIioryrhZJ2ALgbwNMi8j2A7wBcAGAJAE9mmSgiIiIiIiKictRE0K6qL4vICgBuBLAygH4A9lXVCXGWM3NmYNt/IiIiIiIiotREjT+rvvf4NIjIagDGZJ0OIiIiIiIianVWV9WxQR8yaAcgpie6VQHMyjotJSwF83BhdeQ/rVQa92dt4f6sLdyftYX7s7Zwf9YW7s/awv0Z31IAxmlIYF4T1ePL5WygwCcbeWH1cj8rbBw/qg7cn7WF+7O2cH/WFu7P2sL9WVu4P2sL92ciJbdT1fceT0RERERERFSrGLQTERERERER5RSD9upSD+AG5y9VP+7P2sL9WVu4P2sL92dt4f6sLdyftYX7swLYER0RERERERFRTrGknYiIiIiIiCinGLQTERERERER5RSDdiIiIiIiIqKcYtBORERERERElFMM2quEiJwtIiNFpE5EeovItlmniZoTkStEpI+IzBKRiSLSXUQ28MzTU0TU83rYM09HEXlXROY6y7lDRNq17K8hEbneZ18NtT5fVEQeFJEpIjJbRF4XkZU8y+C+zAnnGurdnyoiDzqf89zMMRHZWUTeFpFxzr452PO5iMiNIvKHiMwTkR4i0skzz7Ii8ryIzBSR6SLSVUSW9MyzmYh86dxvR4vIpS3w81qdsP0pIguJyO0iMkBE5jjzPCMiq3qW4XdOX+6Zh/uzBUQ4P5/y2VcfeObh+ZkTEfan371UReQSax6enyli0F4FRORIAHfDDJ+wFYCfAHwoIitmmjDyswuABwFsD2AvAAsB+EhElvDM9xiAVazXnxcpEWkL4F0ACwPYEcAJAE4EcGOF007+BqF4X+1kfXYPgAMBHAGz71cF0M39kPsyd/6K4n25lzP9VWsenpv5tQTM/e/sgM8vBXAegDMBbAdgDsy9clFrnucBbAKz7w8AsDOAR90PRaQ9gI8AjAKwNYBLAFwvIqen+ksICN+fi8Pkd25y/h4KYAMAb/nMey2Kz9kH3A+4P1tUqfMTAD5A8b46yvM5z8/8KLU/V/G8TgagAF73zMfzMy2qylfOXwB6A+hivW8DYCyAy7NOG18l990KMBexna1pPQHcG/KdvwNoBLCSNe1MADMALJz1b2pNLwDXA+gX8FkHAA0ADrembejs7+25L/P/AnAvgGEoDH/Kc7NKXs55drD1XgD8AeBia1oHAHUA/uW838j53jbWPPsCaAKwqvP+3wCm2vsTwG0Ahmb9m2v55d2fAfP81ZmvozVtJIALQr7D/ZmT/QngKQDdQ77D8zOnr4jnZ3cAn3im8fxM8cWS9pwTkYVhnj71cKepapPzfoes0kWRdXD+TvVMP0ZEJovIQBG5VUQWtz7bAcAAVZ1gTfsQQHuYJ9DUsjo51cN+c6rtdXSmbw1Tk8I+N4cC+B2Fc5P7Mqeca+uxAJ5QJ6fg4LlZndYGsDKKz8cZMA+97fNxuqp+b32vB0xQsJ01zxeq2mDN8yGADURkmQqlnaLpABM8TPdMv1xME6UfReQST3MV7s982VVMs6KfReQhEVnO+oznZ5US0yxwfwBdfT7m+ZkStsPLv+UBtAUwwTN9AkypHuWUiLSBKcn7WlUHWh+9AFMVaByAzQDcDlPt71Dn85Xhv7/dz6jl9Iap/vwzTLWu6wB8KSJ/gdkXDao63fOdCSjsJ+7L/DoYwNIwpT8unpvVy93+fvvHPh8n2h+q6gIRmeqZZ4TPMtzPpqWSWorFaeJwO4AXVXWm9dH9AH6AeTC+I4BbYa7VFzmfc3/mxwcwzcdGAFgXwC0A3heRHVS1ETw/q9kJAGbBah7o4PmZIgbtRJXzIIC/oLgNNFT1UevtABH5A8AnIrKuqg5vyQRSOFV933rbX0R6wwR1/wQwL5tUUUpOAfC+qo5zJ/DcJMofEVkIwCswTSD+bX+mqndbb/uLSAOAR0TkClWtb8FkUgmq+pL1doCI9AcwHMCuAD7JJFGUlpMBPK+qdfZEnp/pYvX4/JsMpw2lZ/pKAMa3fHIoChHpAtOJym6qOqbE7L2dv+s5f8fDf3+7n1FGnFL1X2D21XgAC4vI0p7Z7HOT+zKHRGRNAHsCeLzErDw3q4e7/cPuleMBFHXg6lTVXBY8Z3PJCtjXBLCXp5TdT2+YAqm1nPfcnzmlqr/B5HHt6yvPzyojIn+DqZFW6n4K8PwsC4P2nHPaefQFsIc7zal2vQeAXlmli/yJ0QXAIQB2V1VvtR8/Wzh//3D+9gKwqWd0gL0AzAQwOK20UnzO0DPrwuyrvgDmo/jc3ABARxTOTe7LfDoJphrmuyXm28L5y3Mz/0bAZPLs87E9TFtY+3xcWkS2tr63O0xeqLc1z85OsOjaC8DPqsqqmi3ICtg7AdhTVadE+NoWMG2g3WrW3J85JSKrA1gOxddXnp/V5xQAfVX1pwjzbgGen8ll3RMeX6VfAI6E6QH3BJjeNR+BaeexUtZp46vZvvofTCc5u8C0x3FfizmfrwvgGphOzNYCcBBM9bDPrWW0BTAApjOOzQHsA3OBuyXr39faXgDudPblWjDtsT4GMAnACs7nD8FUl9/N2affAPiG+zK/L5gM4CgAt3mm89zM+QvAkjCZvi1gOiS70Pm/o/P5Zc698SAAm8L0ZvwbgEWtZbwP08ZyWwD/B1Nz5gXr8w4wwf8zMJ0LHgkzdNzpWf/+WnuF7U+YTj7fBDDaOdfs++nCzvd3AHCB8/k6AI5xzsenuT9ztz+XBHAHzHC4a8E8XOvrnH+LWMvg+ZmTV6nrrTNPe2f7n+nzfZ6fae+TrBPAV8QdBZwDk9Gsh3niuF3WaeLLdz9pwOtE5/M1AHwOYArMg5hfAXQG0N6znDUBvAdgLkyQeCeAdln/vtb2AvASTKdk9QDGOO/XtT5fFKbvgqnOjaYbgJW5L/P7ArC3c06u75nOczPnL5i2r37X16eczwXAjU4msA6m52nvfl4WpsPBWTBD9T0BYEnPPJsB+NJZxhgAl2X922vxFbY/YQK7oPvprs73twLwLcyD8nkwtV2ugBUEcn/mZn8uBvOwcyLMUKkjYcZfX8mzDJ6fOXmVut4685zu3As7+Hyf52fKL3dsWiIiIiIiIiLKGbZpJyIiIiIiIsopBu1EREREREREOcWgnYiIiIiIiCinGLQTERERERER5RSDdiIiIiIiIqKcYtBORERERERElFMM2omIiIiIiIhyikE7ERERxSYi14tIvzKX0VNE7i0xz0gRuaCc9RAREVWzdlkngIiIiFqtQwHMzzoRREREecagnYiIiDKhqlOzTgMREVHesXo8ERFRDXOqoN8vIp1FZKqIjBeR6z3zdBSRN0VktojMFJFXRGQlzzyXi8gEEZklIl0BLOqzrlNFZIiI1InIUBE5K0La7rXerygib4vIPBEZISLHlPXjiYiIagCDdiIiotp3AoA5ALYDcCmAa0VkLwAQkTYA3gSwLIBdAOwFYB0AL7tfFpF/ArgewJUAtgHwB4CigNwJsG8EcBWAjZx5bxKRE2Kk8ykAawDYDcDhzjpWjPNDiYiIag2rxxMREdW+/qp6g/P/ryJyDoA9AHzs/N0UwNqqOhoAROR4AINE5K+q2gfABQC6qmpXZxlXi8ieKC5tvwHAf1S1m/N+hIhsDOAMAE+XSqCIrA/g7wC2ddYJETkFwJCkP5qIiKgWsKSdiIio9vX3vP8DhRLsjQCMdgN2AFDVwQCmO5+58/T2LKOX+4+ILAFgXQBdnSr2s0VkNoCrnelRbARgAYC+VjqGOukgIiJqtVjSTkREVPu8PbQr0n1wv6Tz9zQ0D+4bU1wPERFRq8OSdiIiotZtCIA1RGQNd4JTrX1pAIOtebbzfG979x9VnQBgHIB1VHWY5zUiYjqGwhQmbG2lYwMnHURERK0WS9qJiIhatx4ABgB4XkQugMkb/A/A56r6vTPPfQCeEpHvAXwN4BgAmwD4zVrOdQDuF5EZAD4AsAhMp3XLqOrdpRKhqj+LyAcAHhGRf8NUlb8XwLyyfyEREVEVY0k7ERFRK6aqCuAfAKYB+AImiP8NwJHWPC8DuAlAZ5g252sCeMiznMcBnArgJJiHAJ8DOBFA1JJ2ON8d53y3G4BHAUyM/6uIiIhqh5h7NRERERERERHlDUvaiYiIiIiIiHKKQTsRERERERFRTjFoJyIiIiIiIsopBu1EREREREREOcWgnYiIiIiIiCinGLQTERERERER5RSDdiIiIiIiIqKcYtBORERERERElFMM2omIiIiIiIhyikE7ERERERERUU4xaCciIiIiIiLKKQbtRERERERERDnFoJ2IiIiIiIgopxi0ExEREREREeUUg3YiIiIiIiKinGLQTkRERERERJRTDNqJiIiIiIiIcopBOxEREREREVFOMWgnIiIiIiIiyikG7UREREREREQ5xaCdiIiIiIiIKKcYtBMRERERERHlFIN2IiIiIiIiopxi0E5ERDVBRHYVERWRXbNOS6WJyHEiMlRE5ovI9IzS8JSIjPRMUxG53jPtryLyjYjMcT7fwpm+r4j0E5E6Z/rSLZX2WiEiI0XkqRSX11NEekaYr9Wca0REecCgnYiIIhORTUTkOREZKyL1IjJORJ4XkU3KXO6VInJwSsmsaSKyIYCnAAwHcBqA0zNNUAgRWQjAqwCWBXAhgOMAjBKR5QC8AmAegLOd6XOySmcYEVlVRK53HzYQERG1tHZZJ4CIiKqDiBwK4EUAUwF0BTACwFoATgFwuIj8S1XfSLj4KwG8BqB7+SmtebvCPHQ/X1WHZZwWr8UALLDerwtgTQCnqerj7kQR2RfAUgCuUdUeLZvE2FYFcB2AkQD6ZZqSyts76wQQEVFzDNqJiKgkEVkXwLMAfgOws6pOsj67D8CXAJ4Vkc1U9beMkpkJEWkDYGFVrWuhVa7o/J2e1gJFZHFVnVvucny2QVBaK/EbllDVXJbWVwtVbcg6DURE1ByrxxMRURSXAFgcwOl2wA4AqjoZwBkAlgBwqTvdr82zM/16EVHrvTrfPcFpJ6ul2umKyOoi0t1pJz1RRO4BsEjAvNuJyAciMkNE5orI5yLyfz7z7Soi3zttrIeLyBnetLrpFZEuInKMiAwCUA9gX+ez1UTkCRGZ4DQfGCQiJ/usaxERuUFEhjnzjRaRziLi+xus740EcIPzdpK3DbmInOWs02268KC3rbjTbnmgiGwtIl+IyFwAt5RY78HOd+qcv4cEzPdnepx9+Lnz0avOZ26b6aed6X28+zvK/nL3i4hsLCIviMg0AF9Znx8rIn1FZJ6ITBWRl0RkjYDtsLGIfOasa6yI2MfwrgD6OG+ftI7PE0O2lZu29ZxzYLrzW54UkcU987YTkWuc461eTBv1W7zHgRhXi8gYJ52fSUCTFBFZWkTudY6peucYu0zMw6VQ4tOmXSKeayLSSUReF5HxznEyxtnuHUqtl4iIwrGknYiIojgQwEhV/dLvQ1X9wgko90+w7OMAPA7gOwCPOtOGB80sIosB+ARARwD3AxjnLGN3n3l3B/A+gL4wwW4TgJMAfCoif1PV75z5tgTwAYA/YKpCtwVwLYBJ3mU6dgfwTwBdAEwGMFJEVgLwLQB1pk8C8HcAXUWkvare66yrDYC3AOzk/N4hADaFafO9PoCDg347gAsAHA/gEAD/BjAbQH9nudc7ae8B4CEAGzjz/FVE/k9V51vLWc7ZLi8BeA7AhKAVisjeAF4HMBjAFc53nwQwJiSdAPAIgLEwTR/uhwl+3fX8DNMW/1qYZhbDnXVF2l+WVwH86qxDnGVcBeAmmDbzjwNYAcC5AL4QkS1Vdbr1/WVg9ns3Z/7DAdwuIgNU9X2YfXMtgBth9pV7/H9T4rfDWd4ImG22FYBTAUwEcJk1z+MAToBpGnIXgO2c+TeC2ceuGwFcDeA957UVgI8ALGyv0Hko8DmA1WC2/+8AdgRwK4BVYI6fyKKeayKyMIAPYYL5BwCMd9JwAIClAcyIs14iIvJQVb744osvvvgKfAHoABOIdi8x35vOfEs575+CCfS9811vbj9F02YDeCpies531nOENW1xmOBNAezqTBMAv8AEZWLNuxhMNf+PrGlvwXSEtqo1bT0A833SqgAaAWzsmf44TFCznGf6izDVwBdz3h/rfH8nz3xnOMvescTvv96Zb3lr2gowJf4fAmhjTT/bmfcka1pPZ9oZEbf3j87v6mBN28tZxkjPvArgeuv9rs60wz3znehM38aaFmd/udvgBc9y14RpU3+lZ/pfnH15pTXN3Q7HWdMWhnlw85o1bRtnvhMjbi83bV0907sBmGy939yZ7zHPfHc403fz7Nt3PNvlv858T1nTroY5lzp5lnmrs13WKJH2ngB6JjjXtvDbz3zxxRdffKXzYvV4IiIqZSnn76wS87mft69gWgBgPziBlTtBTXvsRz3zbQGgE4AXACwnIsuLyPIwVfE/AbCziLQRkbYA9oR5KDHOWuYwmFJfP5+r6mD3jYgIgMMAvO28Xd5a34cwDz62cmY/AqYEd6hnvk+dz3eLtzkAJ/0LA7hXVZus6Y8BmInmNSDqYUrLQ4nIKjDb8WlV/bO0VFU/hil5T9MWiLC/PN952PP+UJimf694tu14mEDTu21nw9Q0APBnm+7vAKyTwu/xpu1LmN/lnh/7OX/v9sx3l/PX3Wfuvn1AVe2mGvf6rPMIZz3TPL+/B0ztkZ1j/oao55p7bOzjbQJARETlY/V4IiIqxQ3GlwqdK3pwX641AQzzBDCAqXJt6+T8fRrBOgBYFKY0168n9qDe2Ud43q8AUw34dAQPweZ2vtYJpvpzUNX7FQOmh1nT+Vu0DVS1QUR+sz53jdVonY653/vV57OfUXgQkYao+2ua9d67HzrBlNj7pRcwpe22MT7H0TQAm4WkIarffZYLmCr5M2G2bRM8x5iqjheR6Shse999oKqTnLb8tk4waU/r2Ip0rqnqCBG5G8BFAI4RkS9haq88Zz/sISKiZBi0ExFRKFWdISJ/oHQgsxlMMDjT/WrAfG1TS1w4t1T2EgQP1TUbJmiPa17Aup5DcNDZ35p3AEyA42d0gvTE5U1/HkTdXza//aAwfQk0Rvi+3zyA0z6+TFGXHXSeJNEGwMcAOgd8/kuK6yqiqv9xOhT8B8zQcfcDuEJEtlfVUv0fEBFRCAbtREQUxTsAThORnVT1K++HIvI3mDHbH7EmT4MpffbylvoC8QKXUQD+IiLiKQHcwDOf25ndTA0ZC1xEJgKog2nD7uU3zc8kmBoGbcPWZaVrcwCf+JRgJjXK+bsBTPtvAH92ELY2TPXocpbbyecz7/YuV6T9FWEZAmCEqqYVoKYZVNtGwQTZnWCaSwAAnA4Nl0Zh29v7wN63K8CU2tuGA1iyjO3nl8Yo5xoAQFUHwDyQullEdgTwNYAzYdraExFRQmzTTkREUdwBU6r5iIgsZ38gIsvCtN+d68znGg6gg4hsZs27Cop7xXbNgX+A7+c9AKvC9PTtLndxNK+W3tdJw8UisqR3IU7QA1VthAlqDxaRVa3P14MpsS3JWcbrAA4Tkb8ErcvxCkzP2qf5zLeYiCwRZZ0ePQA0ADjPaV/vOgWmSvm7CZYJVf0DptT7BHvoLhHZC8DGSZYZItL+KqEbTAn3dZ7t4A6btpz/10K5Y78vneC7Yd5z/l7gme7WwHD3WQ+Yav3nen6T93uAObZ2EJF9vB84Q8HFLayJdK6JSHufZQ+Aqf4fOowhERGVxpJ2IiIqSVV/FZETADwPYICIdIVpT7wWTGC4PICjVNUequ0lALcDeENE7ofpdfrfMFV0vW2h+wLYU0QugumpfISq9g5IzmMAzgHwjIhsDdNR1nEwDw3sNDeJyKkwnckNEpEnYYYgWw2mQ7KZMEPZAabH770BfC0iD8FU4T8HwECYDtKiuNxZbm8ReQymo7Zlnd+6p/M/ADwLM1zcwyKyG0xpZFsAGzrT9wHwfcR1ur91kojcCjPk2wci8hZMaehZMEOtPRf2/RKugAkgvxKRJ5zfcS6AQQCaBddJxdxfQcsYLiJXw/SWvpaIdIepAbE2zMOiRwHcGTNpw2F6/z9TRGbBBPG9VdXbnj4WVf1JRJ4GcLqILA0zVNu2MEPAdVfVz5z5JonInTD74R0ReQ/AljAPlCZ7FnsHgIOc+Z6COa+WgBlS8HCY89X7nTCRzjWYIeC6iMirMOd3O2c+92EWERGVgUE7ERFFoqqvishQmODBDdSnAPgMwC2qOtAz/xQROQSmd+zOKIxZ3QnNg/aLYAKqm2E6hXsagG/QrqpzRWQPmPGgz4UJIJ6HCfY+8MzbU0R2AHANTPCxJExP4r1hVeVX1b4i8neYgO4mmHbl18J0GLdhxO0zQUS2db53KEzAPAUmuL3Mmq9JRA6GGZfdHXN9LkzV5/uQsN2xql4vIpOc33kPgKkw2/RKLR6jPe5yPxCRI2D2za0wQexJMG2Xd0263IB1RdpfJZZxm4j8ArN9r3Mmj4YZ1/ytBGma7zywuhWmRkk7mN9fVtDuOBVmv58IcxyMd9Zzg2e+q2GacJwJ58EQzEOmohoUzrmxC8y49UfAHF8zYY6p6xBzvPQY59pPMKMkHAjzkGWuM+3vqvptnHUSEVFzkl5zOiIiotrilNRuoqp+bbqJiIiIKo5t2omIiGDak3ved4IZp7pnJgkiIiIiAkvaiYiIAADOsHZPwVRXXhOm/f0iALZU1aBxv4mIiIgqim3aiYiIjA8AHAVgZQD1AHrBtAdnwE5ERESZYUk7ERERERERUU6xTTsRERERERFRTjFoJyIiIiIiIsoptmkHICICYFUAs7JOCxEREREREbUaSwEYpyHt1hm0G6sCGJN1IoiIiIiIiKjVWR3A2KAPGbQbswBg9OjRaN++fdZpISIiIiIioho3c+ZMrLHGGkCJGt8M2i3t27dn0E5ERERERES5wY7oiIiIiIiIiHKKQTsRERERERFRTjFoJyIiIiIiakUmz52Ms989G33H9c06KRQBg3YiIiIiIqJW5Kx3z8L/vv8ftnlsm6yTQhEwaCciIiIiImpFBkwckHUSKAYG7UREREREREQ5xaCdiIiIiIiIKKcYtBMRERERERHlFIN2IiIiIiIiopxi0E5ERERERESUU7GDdhG5VkQW95m+mIhcG3NZ/xaR/iIy03n1EpG/W58vKiIPisgUEZktIq+LyEqeZXQUkXdFZK6ITBSRO0SkXdzfRURERERERJQ3SUrarwOwpM/0xZ3P4hgD4HIAWwPYBsCnAN4UkU2cz+8BcCCAIwDsAmBVAN3cL4tIWwDvAlgYwI4ATgBwIoAbY6aDiIhqRJM24b1f38OE2ROyTgoRERFR2ZIE7QJAfaZvDmBqnAWp6tuq+p6q/qqqv6jqVQBmA9heRDoAOAXARar6qar2BXASgB1FZHtnEXsD2BjAsaraT1XfB3ANgLNFZOEEv42IiKrc0/2exv4v7I+NHtwo66QQERERlS1y0C4i00RkKkzA/ouITLVeMwB8DOCVpAkRkbYi8i8ASwDoBVP6vhCAHu48qjoUwO8AdnAm7QBggKraxSkfAmgPYBMEEJFFRKS9+wKwVNJ0ExFRvrzz6zsAgGl10zJOCREREVH54rT9vgCmlP0JmGrwM6zPGgCMVNVecRMgIpvCBOmLwpSyH6Kqg0VkCwANqjrd85UJAFZ2/l/Zee/9HNY8fq5A/Kr8RERERERERC0qctCuqk8DgIiMAPCNqs5PKQ0/A9gCQAcAhwN4WkR2SWnZQW4FcLf1fimY9vVEREREREREuRG7l3VV/VxE2ojI+gBWhKeKvap+EXN5DQCGOW/7ishfAZwP4GUAC4vI0p7S9pUAjHf+Hw9gW88iV7I+C1pnPYB6972IxEkyERERERERUYuIHbQ7ncC9AGBNmOryNgXQtsw0tQGwCIC+AOYD2APA6866NwDQEaY6PZy/V4nIiqo60Zm2F4CZAAaXmQ4iIqpCqn59pRIRERFVpyTjmT8M4HsA+wP4A/49yUciIrcCeB+mc7mlABwNYFcA+6jqDBHpCuBupwO8mQAeANBLVb91FvERTHD+rIhcCtOO/WYADzql6URERERERGThA+7qkiRo7wTgcFUdVnLO0lYE8AyAVWA6tusPE7B/7Hx+IYAmmJL2RWB6hj/L/bKqNorIAQAegil1nwPgaQDXppA2IiIiIiIiokwlCdp7A1gPhXboianqKSU+rwNwtvMKmmcUgP3KTQsRERERERFR3iQJ2h8AcJeIrAxgAEy78z+pav80EkZERERERETU2iUJ2l93/j5hTVOYTunS6IiOiIiIiIiIiJAsaF879VQQERERERERUTNJxmkfVYmEEBEREREREVGxJOO0Hx/2uao+kzw5RERE5dHkI5ESERER5U6S6vH3ed4vBGBxAA0A5sIM4UZEREREREREZUpSPX4Z7zQR6QQzVvodaSSKiIiIiIiIiIA2aSxEVX8FcDmal8ITERERERERUUKpBO2OBQBWTXF5RERERERERK1ako7oDvJOArAKgHMAfJ1GooiIKBlVhYhknQwiIiIiSkmSjui6e94rgEkAPgXwn3ITREREydzf+37c8PkN6HlCT2y60qZZJyczquw9noiIiGpHko7o0qxST0REKTn/g/MBAKe/czp6ndIr49QQERERURrKCsDFkVZiiIiIysVx2omIiKiWJAraReR4ERkAYB6AeSLSX0SOSzdpRERERERERK1bko7oLgJwE4AuKHQ8txOAh0VkeVW9J8X0EREREREREbVaSTqiOxfAv1X1GWvaWyIyCMD1ABi0ExEREREREaUgSfX4VQB84zP9G+czIiIiIiIiIkpBkqB9GIB/+kw/EsCv5SWHiIiIiIiIiFxJqsdfB+BlEdkZhTbt/wdgD/gH80REVIVUFTPqZ2DpRZfOOilERERErVbsknZVfR3AdgAmAzjYeU0GsK2qvpFm4oiIKDvHdDsGy9y+DHqN5pjvRERERFlJUtIOVe0L4NiU00JERDny4sAXAQCdv+mMN46snmeyqhynnYiIKIyC98pqErukXUT2E5F9fKbvIyJ/TydZRET598xPz+Cc985BkzZlnRQiIiIiqlFJOqK7DUBbn+nifEZE1Cqc0P0EPNjnQbz181tZJ4WIiIiIalSSoL0TgME+04cCWK+85BARVZ8pc6dknQQiIiIiqlFJgvYZANbxmb4egDnlJYeIyHhxwIvoM7ZP1skgIiIiIspUkqD9TQD3isi67gQRWQ/AXQBYR5SIytZrdC8c3e1obPv4tlknJRIRyToJRdgRGxEREVHtSBK0XwpToj5UREaIyAgAQwBMAXBxmokjotZp0KRBWSeBiIiIiCgXYg/5pqozRGRHAHsB2BzAPAD9VfWLtBNHRK1TY1Nj1kmoankr+SciIiKi5JKO064APnJeRESpWtC0IOskUBXj2LNERERUS5JUj0+NiFwhIn1EZJaITBSR7iKygWeeRUXkQRGZIiKzReR1EVnJM09HEXlXROY6y7lDRBI9kCCi7DVqdZW0C/JVss027URERES1I9OgHcAuAB4EsD1MdfuFAHwkIktY89wD4EAARzjzrwqgm/uhiLQF8C6AhQHsCOAEACcCuLHyySeiSmD1eCIiIiIiI9PSaFXd134vIicCmAhgawBf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\n",
      "text/plain": [
       "<Figure size 1200x800 with 3 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "num_of_nodes = len(node_labels)\n",
    "plot_in_out_degree_distributions(edge_index, num_of_nodes, config['dataset_name'])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ordinary-quantity",
   "metadata": {},
   "source": [
    "You can immediately notice a couple of things:\n",
    "* The top 2 plots are the same, because we treat PPI as an undirected graph\n",
    "* Many more nodes have a large number of edges (compared to Cora), but still, most nodes have far less edges\n",
    "* The third plot nicely visualizes this in the form of a histogram - most nodes have only `1-20` edges (hence the peak on the leftmost side). But it's **more spread out compared to Cora.**\n",
    "\n",
    "Ok, we're starting to get some valuable insight into PPI, let's continue further and literally visualize/see PPI's graphs.\n",
    "\n",
    "The following cell will plot the training graph we fetched in one of the previous cells, run it. (*whispers: run it*)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "lyric-coordinate",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Check out this blog for available graph visualization tools:\n",
    "    https://towardsdatascience.com/large-graph-visualization-tools-and-approaches-2b8758a1cd59\n",
    "\n",
    "Basically depending on how big your graph is there may be better drawing tools than igraph.\n",
    "\n",
    "Note: I unfortunatelly had to flatten this function since igraph is having some problems with Jupyter Notebook,\n",
    "we'll only call it here so it's fine!\n",
    "\n",
    "\"\"\"\n",
    "\n",
    "dataset_name = config['dataset_name']\n",
    "visualization_tool=GraphVisualizationTool.IGRAPH\n",
    "\n",
    "if isinstance(edge_index, torch.Tensor):\n",
    "    edge_index_np = edge_index.cpu().numpy()\n",
    "\n",
    "if isinstance(node_labels, torch.Tensor):\n",
    "    node_labels_np = node_labels.cpu().numpy()\n",
    "\n",
    "num_of_nodes = len(node_labels_np)\n",
    "edge_index_tuples = list(zip(edge_index_np[0, :], edge_index_np[1, :]))  # igraph requires this format\n",
    "\n",
    "# Construct the igraph graph\n",
    "ig_graph = ig.Graph()\n",
    "ig_graph.add_vertices(num_of_nodes)\n",
    "ig_graph.add_edges(edge_index_tuples)\n",
    "\n",
    "# Prepare the visualization settings dictionary\n",
    "visual_style = {}\n",
    "\n",
    "# Defines the size of the plot and margins\n",
    "visual_style[\"bbox\"] = (650, 650)\n",
    "visual_style[\"margin\"] = 5\n",
    "\n",
    "# I've chosen the edge thickness such that it's proportional to the number of shortest paths (geodesics)\n",
    "# that go through a certain edge in our graph (edge_betweenness function, a simple ad hoc heuristic)\n",
    "\n",
    "# line1: I use log otherwise some edges will be too thick and others not visible at all\n",
    "# edge_betweeness returns < 1 for certain edges that's why I use clip as log would be negative for those edges\n",
    "# line2: Normalize so that the thickest edge is 1 otherwise edges appear too thick on the chart\n",
    "# line3: The idea here is to make the strongest edge stay stronger than others, 6 just worked, don't dwell on it\n",
    "\n",
    "edge_weights_raw = np.clip(np.log(np.asarray(ig_graph.edge_betweenness())+1e-16), a_min=0, a_max=None)\n",
    "edge_weights_raw_normalized = edge_weights_raw / np.max(edge_weights_raw)\n",
    "edge_weights = [w**6 for w in edge_weights_raw_normalized]\n",
    "visual_style[\"edge_width\"] = edge_weights\n",
    "\n",
    "# A simple heuristic for vertex size.\n",
    "visual_style[\"vertex_size\"] = [deg / 10 for deg in ig_graph.degree()]\n",
    "\n",
    "# Set the layout - the way the graph is presented on a 2D chart. Graph drawing is a subfield for itself!\n",
    "# I used \"Kamada Kawai\" a force-directed method, this family of methods are based on physical system simulation.\n",
    "visual_style[\"layout\"] = ig_graph.layout_kamada_kawai()\n",
    "\n",
    "print('Plotting results ... (it may take couple of seconds).')\n",
    "ig.plot(ig_graph, **visual_style)\n",
    "\n",
    "# This website has got some awesome visualizations check it out:\n",
    "# http://networkrepository.com/graphvis.php?d=./data/gsm50/labeled/cora.edges"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "artistic-alpha",
   "metadata": {},
   "source": [
    "<img src=\"data/readme_pics/ppi_graph_jupyter.PNG\" alt=\"PPI visualized\" align=\"center\"/> <br/>\n",
    "\n",
    "**Note: I had to clear the original output of this cell when checking this notebook in, otherwise the file is huuge!** <br/>\n",
    "**I'm just showing you one arbitrary PPI training graph example here, yours may be different (there are 20 training graphs).**\n",
    "\n",
    "And I don't know about you, but I think this one is also beautiful! And it's **very different from Cora.** We can see much more edges.\n",
    "\n",
    "There are also multiple big nodes of similar size whereas in Cora a single node dominates the plot.\n",
    "\n",
    "Ok, we're done with visualizations and understanding our data. This is a huge milestone, so tap yourself on the back. 🏆🎂🎵\n",
    "\n",
    "We have the level 2 unlocked (the GAT model 🦄). 😍\n",
    "\n",
    "And now, let's understand the model! (just **skip the Part 2** and run the cells blindly **if you already went through the 1st part** of this GNN notebook series)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "emotional-regulation",
   "metadata": {},
   "source": [
    "# Part 2: Understanding GAT's inner workings 💻🦄\n",
    "\n",
    "First let's create a high level class where we'll build up `GAT` from `GatLayer` objects. \n",
    "\n",
    "It basically just stacks the layers into a nn.Sequential object and additionally since nn.Sequential expects a single input (and it has a single output) I just pack the data (features, edge index) into a tuple - *pure syntactic sugar*. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "hourly-alert",
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch.nn as nn\n",
    "from torch.optim import Adam\n",
    "\n",
    "\n",
    "class GAT(torch.nn.Module):\n",
    "    \"\"\"\n",
    "    The most interesting and hardest implementation is implementation #3.\n",
    "    Imp1 and imp2 differ in subtle details but are basically the same thing.\n",
    "\n",
    "    So I'll focus on imp #3 in this notebook.\n",
    "\n",
    "    \"\"\"\n",
    "\n",
    "    def __init__(self, num_of_layers, num_heads_per_layer, num_features_per_layer, add_skip_connection=True, bias=True,\n",
    "                 dropout=0.6, log_attention_weights=False):\n",
    "        super().__init__()\n",
    "        assert num_of_layers == len(num_heads_per_layer) == len(num_features_per_layer) - 1, f'Enter valid arch params.'\n",
    "\n",
    "        num_heads_per_layer = [1] + num_heads_per_layer  # trick - so that I can nicely create GAT layers below\n",
    "\n",
    "        gat_layers = []  # collect GAT layers\n",
    "        for i in range(num_of_layers):\n",
    "            layer = GATLayer(\n",
    "                num_in_features=num_features_per_layer[i] * num_heads_per_layer[i],  # consequence of concatenation\n",
    "                num_out_features=num_features_per_layer[i+1],\n",
    "                num_of_heads=num_heads_per_layer[i+1],\n",
    "                concat=True if i < num_of_layers - 1 else False,  # last GAT layer does mean avg, the others do concat\n",
    "                activation=nn.ELU() if i < num_of_layers - 1 else None,  # last layer just outputs raw scores\n",
    "                dropout_prob=dropout,\n",
    "                add_skip_connection=add_skip_connection,\n",
    "                bias=bias,\n",
    "                log_attention_weights=log_attention_weights\n",
    "            )\n",
    "            gat_layers.append(layer)\n",
    "\n",
    "        self.gat_net = nn.Sequential(\n",
    "            *gat_layers,\n",
    "        )\n",
    "\n",
    "    # data is just a (in_nodes_features, edge_index) tuple, I had to do it like this because of the nn.Sequential:\n",
    "    # https://discuss.pytorch.org/t/forward-takes-2-positional-arguments-but-3-were-given-for-nn-sqeuential-with-linear-layers/65698\n",
    "    def forward(self, data):\n",
    "        return self.gat_net(data)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "turkish-formula",
   "metadata": {},
   "source": [
    "Now for the fun part let's define the layer. \n",
    "\n",
    "I really don't think that I can explain it any better, using words, than you taking your time to digest the code and the comments.\n",
    "\n",
    "Also make sure to check out [my video on GAT](https://www.youtube.com/watch?v=uFLeKkXWq2c) before you start losing time trying to figure it out \"from scratch\". It's always good to have some theoretical background at your hand."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "outer-monitoring",
   "metadata": {},
   "outputs": [],
   "source": [
    "class GATLayer(torch.nn.Module):\n",
    "    \"\"\"\n",
    "    Implementation #3 was inspired by PyTorch Geometric: https://github.com/rusty1s/pytorch_geometric\n",
    "\n",
    "    But, it's hopefully much more readable! (and of similar performance)\n",
    "\n",
    "    \"\"\"\n",
    "    \n",
    "    # We'll use these constants in many functions so just extracting them here as member fields\n",
    "    src_nodes_dim = 0  # position of source nodes in edge index\n",
    "    trg_nodes_dim = 1  # position of target nodes in edge index\n",
    "\n",
    "    # These may change in the inductive setting - leaving it like this for now (not future proof)\n",
    "    nodes_dim = 0      # node dimension (axis is maybe a more familiar term nodes_dim is the position of \"N\" in tensor)\n",
    "    head_dim = 1       # attention head dim\n",
    "\n",
    "    def __init__(self, num_in_features, num_out_features, num_of_heads, concat=True, activation=nn.ELU(),\n",
    "                 dropout_prob=0.6, add_skip_connection=True, bias=True, log_attention_weights=False):\n",
    "\n",
    "        super().__init__()\n",
    "\n",
    "        self.num_of_heads = num_of_heads\n",
    "        self.num_out_features = num_out_features\n",
    "        self.concat = concat  # whether we should concatenate or average the attention heads\n",
    "        self.add_skip_connection = add_skip_connection\n",
    "\n",
    "        #\n",
    "        # Trainable weights: linear projection matrix (denoted as \"W\" in the paper), attention target/source\n",
    "        # (denoted as \"a\" in the paper) and bias (not mentioned in the paper but present in the official GAT repo)\n",
    "        #\n",
    "\n",
    "        # You can treat this one matrix as num_of_heads independent W matrices\n",
    "        self.linear_proj = nn.Linear(num_in_features, num_of_heads * num_out_features, bias=False)\n",
    "\n",
    "        # After we concatenate target node (node i) and source node (node j) we apply the \"additive\" scoring function\n",
    "        # which gives us un-normalized score \"e\". Here we split the \"a\" vector - but the semantics remain the same.\n",
    "        # Basically instead of doing [x, y] (concatenation, x/y are node feature vectors) and dot product with \"a\"\n",
    "        # we instead do a dot product between x and \"a_left\" and y and \"a_right\" and we sum them up\n",
    "        self.scoring_fn_target = nn.Parameter(torch.Tensor(1, num_of_heads, num_out_features))\n",
    "        self.scoring_fn_source = nn.Parameter(torch.Tensor(1, num_of_heads, num_out_features))\n",
    "\n",
    "        # Bias is definitely not crucial to GAT - feel free to experiment (I pinged the main author, Petar, on this one)\n",
    "        if bias and concat:\n",
    "            self.bias = nn.Parameter(torch.Tensor(num_of_heads * num_out_features))\n",
    "        elif bias and not concat:\n",
    "            self.bias = nn.Parameter(torch.Tensor(num_out_features))\n",
    "        else:\n",
    "            self.register_parameter('bias', None)\n",
    "\n",
    "        if add_skip_connection:\n",
    "            self.skip_proj = nn.Linear(num_in_features, num_of_heads * num_out_features, bias=False)\n",
    "        else:\n",
    "            self.register_parameter('skip_proj', None)\n",
    "\n",
    "        #\n",
    "        # End of trainable weights\n",
    "        #\n",
    "\n",
    "        self.leakyReLU = nn.LeakyReLU(0.2)  # using 0.2 as in the paper, no need to expose every setting\n",
    "        self.activation = activation\n",
    "        # Probably not the nicest design but I use the same module in 3 locations, before/after features projection\n",
    "        # and for attention coefficients. Functionality-wise it's the same as using independent modules.\n",
    "        self.dropout = nn.Dropout(p=dropout_prob)\n",
    "\n",
    "        self.log_attention_weights = log_attention_weights  # whether we should log the attention weights\n",
    "        self.attention_weights = None  # for later visualization purposes, I cache the weights here\n",
    "\n",
    "        self.init_params()\n",
    "        \n",
    "    def forward(self, data):\n",
    "        #\n",
    "        # Step 1: Linear Projection + regularization\n",
    "        #\n",
    "\n",
    "        in_nodes_features, edge_index = data  # unpack data\n",
    "        num_of_nodes = in_nodes_features.shape[self.nodes_dim]\n",
    "        assert edge_index.shape[0] == 2, f'Expected edge index with shape=(2,E) got {edge_index.shape}'\n",
    "\n",
    "        # shape = (N, FIN) where N - number of nodes in the graph, FIN - number of input features per node\n",
    "        # We apply the dropout to all of the input node features (as mentioned in the paper)\n",
    "        in_nodes_features = self.dropout(in_nodes_features)\n",
    "\n",
    "        # shape = (N, FIN) * (FIN, NH*FOUT) -> (N, NH, FOUT) where NH - number of heads, FOUT - num of output features\n",
    "        # We project the input node features into NH independent output features (one for each attention head)\n",
    "        nodes_features_proj = self.linear_proj(in_nodes_features).view(-1, self.num_of_heads, self.num_out_features)\n",
    "\n",
    "        nodes_features_proj = self.dropout(nodes_features_proj)  # in the official GAT imp they did dropout here as well\n",
    "\n",
    "        #\n",
    "        # Step 2: Edge attention calculation\n",
    "        #\n",
    "\n",
    "        # Apply the scoring function (* represents element-wise (a.k.a. Hadamard) product)\n",
    "        # shape = (N, NH, FOUT) * (1, NH, FOUT) -> (N, NH, 1) -> (N, NH) because sum squeezes the last dimension\n",
    "        # Optimization note: torch.sum() is as performant as .sum() in my experiments\n",
    "        scores_source = (nodes_features_proj * self.scoring_fn_source).sum(dim=-1)\n",
    "        scores_target = (nodes_features_proj * self.scoring_fn_target).sum(dim=-1)\n",
    "\n",
    "        # We simply copy (lift) the scores for source/target nodes based on the edge index. Instead of preparing all\n",
    "        # the possible combinations of scores we just prepare those that will actually be used and those are defined\n",
    "        # by the edge index.\n",
    "        # scores shape = (E, NH), nodes_features_proj_lifted shape = (E, NH, FOUT), E - number of edges in the graph\n",
    "        scores_source_lifted, scores_target_lifted, nodes_features_proj_lifted = self.lift(scores_source, scores_target, nodes_features_proj, edge_index)\n",
    "        scores_per_edge = self.leakyReLU(scores_source_lifted + scores_target_lifted)\n",
    "\n",
    "        # shape = (E, NH, 1)\n",
    "        attentions_per_edge = self.neighborhood_aware_softmax(scores_per_edge, edge_index[self.trg_nodes_dim], num_of_nodes)\n",
    "        # Add stochasticity to neighborhood aggregation\n",
    "        attentions_per_edge = self.dropout(attentions_per_edge)\n",
    "\n",
    "        #\n",
    "        # Step 3: Neighborhood aggregation\n",
    "        #\n",
    "\n",
    "        # Element-wise (aka Hadamard) product. Operator * does the same thing as torch.mul\n",
    "        # shape = (E, NH, FOUT) * (E, NH, 1) -> (E, NH, FOUT), 1 gets broadcast into FOUT\n",
    "        nodes_features_proj_lifted_weighted = nodes_features_proj_lifted * attentions_per_edge\n",
    "\n",
    "        # This part sums up weighted and projected neighborhood feature vectors for every target node\n",
    "        # shape = (N, NH, FOUT)\n",
    "        out_nodes_features = self.aggregate_neighbors(nodes_features_proj_lifted_weighted, edge_index, in_nodes_features, num_of_nodes)\n",
    "\n",
    "        #\n",
    "        # Step 4: Residual/skip connections, concat and bias\n",
    "        #\n",
    "\n",
    "        out_nodes_features = self.skip_concat_bias(attentions_per_edge, in_nodes_features, out_nodes_features)\n",
    "        return (out_nodes_features, edge_index)\n",
    "\n",
    "    #\n",
    "    # Helper functions (without comments there is very little code so don't be scared!)\n",
    "    #\n",
    "\n",
    "    def neighborhood_aware_softmax(self, scores_per_edge, trg_index, num_of_nodes):\n",
    "        \"\"\"\n",
    "        As the fn name suggest it does softmax over the neighborhoods. Example: say we have 5 nodes in a graph.\n",
    "        Two of them 1, 2 are connected to node 3. If we want to calculate the representation for node 3 we should take\n",
    "        into account feature vectors of 1, 2 and 3 itself. Since we have scores for edges 1-3, 2-3 and 3-3\n",
    "        in scores_per_edge variable, this function will calculate attention scores like this: 1-3/(1-3+2-3+3-3)\n",
    "        (where 1-3 is overloaded notation it represents the edge 1-3 and its (exp) score) and similarly for 2-3 and 3-3\n",
    "         i.e. for this neighborhood we don't care about other edge scores that include nodes 4 and 5.\n",
    "\n",
    "        Note:\n",
    "        Subtracting the max value from logits doesn't change the end result but it improves the numerical stability\n",
    "        and it's a fairly common \"trick\" used in pretty much every deep learning framework.\n",
    "        Check out this link for more details:\n",
    "\n",
    "        https://stats.stackexchange.com/questions/338285/how-does-the-subtraction-of-the-logit-maximum-improve-learning\n",
    "\n",
    "        \"\"\"\n",
    "        # Calculate the numerator. Make logits <= 0 so that e^logit <= 1 (this will improve the numerical stability)\n",
    "        scores_per_edge = scores_per_edge - scores_per_edge.max()\n",
    "        exp_scores_per_edge = scores_per_edge.exp()  # softmax\n",
    "\n",
    "        # Calculate the denominator. shape = (E, NH)\n",
    "        neigborhood_aware_denominator = self.sum_edge_scores_neighborhood_aware(exp_scores_per_edge, trg_index, num_of_nodes)\n",
    "\n",
    "        # 1e-16 is theoretically not needed but is only there for numerical stability (avoid div by 0) - due to the\n",
    "        # possibility of the computer rounding a very small number all the way to 0.\n",
    "        attentions_per_edge = exp_scores_per_edge / (neigborhood_aware_denominator + 1e-16)\n",
    "\n",
    "        # shape = (E, NH) -> (E, NH, 1) so that we can do element-wise multiplication with projected node features\n",
    "        return attentions_per_edge.unsqueeze(-1)\n",
    "\n",
    "    def sum_edge_scores_neighborhood_aware(self, exp_scores_per_edge, trg_index, num_of_nodes):\n",
    "        # The shape must be the same as in exp_scores_per_edge (required by scatter_add_) i.e. from E -> (E, NH)\n",
    "        trg_index_broadcasted = self.explicit_broadcast(trg_index, exp_scores_per_edge)\n",
    "\n",
    "        # shape = (N, NH), where N is the number of nodes and NH the number of attention heads\n",
    "        size = list(exp_scores_per_edge.shape)  # convert to list otherwise assignment is not possible\n",
    "        size[self.nodes_dim] = num_of_nodes\n",
    "        neighborhood_sums = torch.zeros(size, dtype=exp_scores_per_edge.dtype, device=exp_scores_per_edge.device)\n",
    "\n",
    "        # position i will contain a sum of exp scores of all the nodes that point to the node i (as dictated by the\n",
    "        # target index)\n",
    "        neighborhood_sums.scatter_add_(self.nodes_dim, trg_index_broadcasted, exp_scores_per_edge)\n",
    "\n",
    "        # Expand again so that we can use it as a softmax denominator. e.g. node i's sum will be copied to\n",
    "        # all the locations where the source nodes pointed to i (as dictated by the target index)\n",
    "        # shape = (N, NH) -> (E, NH)\n",
    "        return neighborhood_sums.index_select(self.nodes_dim, trg_index)\n",
    "\n",
    "    def aggregate_neighbors(self, nodes_features_proj_lifted_weighted, edge_index, in_nodes_features, num_of_nodes):\n",
    "        size = list(nodes_features_proj_lifted_weighted.shape)  # convert to list otherwise assignment is not possible\n",
    "        size[self.nodes_dim] = num_of_nodes  # shape = (N, NH, FOUT)\n",
    "        out_nodes_features = torch.zeros(size, dtype=in_nodes_features.dtype, device=in_nodes_features.device)\n",
    "\n",
    "        # shape = (E) -> (E, NH, FOUT)\n",
    "        trg_index_broadcasted = self.explicit_broadcast(edge_index[self.trg_nodes_dim], nodes_features_proj_lifted_weighted)\n",
    "        # aggregation step - we accumulate projected, weighted node features for all the attention heads\n",
    "        # shape = (E, NH, FOUT) -> (N, NH, FOUT)\n",
    "        out_nodes_features.scatter_add_(self.nodes_dim, trg_index_broadcasted, nodes_features_proj_lifted_weighted)\n",
    "\n",
    "        return out_nodes_features\n",
    "\n",
    "    def lift(self, scores_source, scores_target, nodes_features_matrix_proj, edge_index):\n",
    "        \"\"\"\n",
    "        Lifts i.e. duplicates certain vectors depending on the edge index.\n",
    "        One of the tensor dims goes from N -> E (that's where the \"lift\" comes from).\n",
    "\n",
    "        \"\"\"\n",
    "        src_nodes_index = edge_index[self.src_nodes_dim]\n",
    "        trg_nodes_index = edge_index[self.trg_nodes_dim]\n",
    "\n",
    "        # Using index_select is faster than \"normal\" indexing (scores_source[src_nodes_index]) in PyTorch!\n",
    "        scores_source = scores_source.index_select(self.nodes_dim, src_nodes_index)\n",
    "        scores_target = scores_target.index_select(self.nodes_dim, trg_nodes_index)\n",
    "        nodes_features_matrix_proj_lifted = nodes_features_matrix_proj.index_select(self.nodes_dim, src_nodes_index)\n",
    "\n",
    "        return scores_source, scores_target, nodes_features_matrix_proj_lifted\n",
    "\n",
    "    def explicit_broadcast(self, this, other):\n",
    "        # Append singleton dimensions until this.dim() == other.dim()\n",
    "        for _ in range(this.dim(), other.dim()):\n",
    "            this = this.unsqueeze(-1)\n",
    "\n",
    "        # Explicitly expand so that shapes are the same\n",
    "        return this.expand_as(other)\n",
    "\n",
    "    def init_params(self):\n",
    "        \"\"\"\n",
    "        The reason we're using Glorot (aka Xavier uniform) initialization is because it's a default TF initialization:\n",
    "            https://stackoverflow.com/questions/37350131/what-is-the-default-variable-initializer-in-tensorflow\n",
    "\n",
    "        The original repo was developed in TensorFlow (TF) and they used the default initialization.\n",
    "        Feel free to experiment - there may be better initializations depending on your problem.\n",
    "\n",
    "        \"\"\"\n",
    "        nn.init.xavier_uniform_(self.linear_proj.weight)\n",
    "        nn.init.xavier_uniform_(self.scoring_fn_target)\n",
    "        nn.init.xavier_uniform_(self.scoring_fn_source)\n",
    "\n",
    "        if self.bias is not None:\n",
    "            torch.nn.init.zeros_(self.bias)\n",
    "\n",
    "    def skip_concat_bias(self, attention_coefficients, in_nodes_features, out_nodes_features):\n",
    "        if self.log_attention_weights:  # potentially log for later visualization in playground.py\n",
    "            self.attention_weights = attention_coefficients\n",
    "\n",
    "        if self.add_skip_connection:  # add skip or residual connection\n",
    "            if out_nodes_features.shape[-1] == in_nodes_features.shape[-1]:  # if FIN == FOUT\n",
    "                # unsqueeze does this: (N, FIN) -> (N, 1, FIN), out features are (N, NH, FOUT) so 1 gets broadcast to NH\n",
    "                # thus we're basically copying input vectors NH times and adding to processed vectors\n",
    "                out_nodes_features += in_nodes_features.unsqueeze(1)\n",
    "            else:\n",
    "                # FIN != FOUT so we need to project input feature vectors into dimension that can be added to output\n",
    "                # feature vectors. skip_proj adds lots of additional capacity which may cause overfitting.\n",
    "                out_nodes_features += self.skip_proj(in_nodes_features).view(-1, self.num_of_heads, self.num_out_features)\n",
    "\n",
    "        if self.concat:\n",
    "            # shape = (N, NH, FOUT) -> (N, NH*FOUT)\n",
    "            out_nodes_features = out_nodes_features.view(-1, self.num_of_heads * self.num_out_features)\n",
    "        else:\n",
    "            # shape = (N, NH, FOUT) -> (N, FOUT)\n",
    "            out_nodes_features = out_nodes_features.mean(dim=self.head_dim)\n",
    "\n",
    "        if self.bias is not None:\n",
    "            out_nodes_features += self.bias\n",
    "\n",
    "        return out_nodes_features if self.activation is None else self.activation(out_nodes_features)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "private-cemetery",
   "metadata": {},
   "source": [
    "The main idea that leads to huge savings is that we calculate the scores only for the nodes that will actually be used and not for every imaginable combination (that would be valid only in a fully-connected graph).\n",
    "\n",
    "Once we compute the `\"left\"` scores and the `\"right\"` scores, we \"lift\" them up using the edge index. That way\n",
    "if the edge `1->2` is not present in the graph we won't have those score pairs in our data structure.\n",
    "\n",
    "After adding lifted \"left\" and \"right\" (or maybe a better naming would be source and target) scores we do smart `neighborhood-aware softmax` - so that the semantics of GAT is respected. After doing the `scatter add` (which you should take your time to understand and go through the docs) we can combine the projected feature vectors, and voilà, we got ourselves a fully-blown GAT layer.\n",
    "\n",
    "---\n",
    "\n",
    "Take your time and **be patient**! Especially if you're new to GNNs. \n",
    "\n",
    "I didn't learn all of this in 1 day, it takes time for the knowledge to sink in. You'll get there as well! ❤️ (if you're not already there 😜)\n",
    "\n",
    "Having said that, we've got the level 3 unlocked (model training 💪). 😍\n",
    "\n",
    "We have the data 📜 ready, we have the GAT model 🦄 ready, let's start training this beast! 💪"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "urban-involvement",
   "metadata": {},
   "source": [
    "# Part 3: Training GAT 💪 (Multi-label classification on PPI!)\n",
    "\n",
    "Phew, well the hardest part is behind us. Let's know create a simple training loop where the goal is to learn to assign multiple labels to PPI nodes.\n",
    "\n",
    "But first let's define some relevant constants. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "dense-criticism",
   "metadata": {},
   "outputs": [],
   "source": [
    "from torch.utils.tensorboard import SummaryWriter\n",
    "\n",
    "\n",
    "# 3 different model training/eval phases used in train.py\n",
    "class LoopPhase(enum.Enum):\n",
    "    TRAIN = 0,\n",
    "    VAL = 1,\n",
    "    TEST = 2\n",
    "\n",
    "    \n",
    "writer = SummaryWriter()  # (tensorboard) writer will output to ./runs/ directory by default\n",
    "\n",
    "\n",
    "# Global vars used for early stopping. After some number of epochs (as defined by the patience_period var) without any\n",
    "# improvement on the validation dataset (measured via micro-F1 metric), we'll break out from the training loop.\n",
    "BEST_VAL_MICRO_F1 = 0\n",
    "BEST_VAL_LOSS = 0\n",
    "PATIENCE_CNT = 0\n",
    "\n",
    "BINARIES_PATH = os.path.join(os.getcwd(), 'models', 'binaries')\n",
    "CHECKPOINTS_PATH = os.path.join(os.getcwd(), 'models', 'checkpoints')\n",
    "\n",
    "# Make sure these exist as the rest of the code assumes it\n",
    "os.makedirs(BINARIES_PATH, exist_ok=True)\n",
    "os.makedirs(CHECKPOINTS_PATH, exist_ok=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "hungry-ebony",
   "metadata": {},
   "source": [
    "Also, let's define a couple of functions that will be useful while training the model.\n",
    "\n",
    "The training state contains a lot of useful `metadata` which we can later use. You can imagine that saving the test accuracy of your model is important, especially when you're training your models on a cloud - it makes the organization so much better."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "cardiac-federal",
   "metadata": {},
   "outputs": [],
   "source": [
    "import git\n",
    "import re  # regex\n",
    "\n",
    "\n",
    "def get_training_state(training_config, model):\n",
    "    training_state = {\n",
    "        \"commit_hash\": git.Repo(search_parent_directories=True).head.object.hexsha,\n",
    "\n",
    "        # Training details\n",
    "        \"dataset_name\": training_config['dataset_name'],\n",
    "        \"num_of_epochs\": training_config['num_of_epochs'],\n",
    "        \"test_perf\": training_config['test_perf'],\n",
    "\n",
    "        # Model structure\n",
    "        \"num_of_layers\": training_config['num_of_layers'],\n",
    "        \"num_heads_per_layer\": training_config['num_heads_per_layer'],\n",
    "        \"num_features_per_layer\": training_config['num_features_per_layer'],\n",
    "        \"add_skip_connection\": training_config['add_skip_connection'],\n",
    "        \"bias\": training_config['bias'],\n",
    "        \"dropout\": training_config['dropout'],\n",
    "\n",
    "        # Model state\n",
    "        \"state_dict\": model.state_dict()\n",
    "    }\n",
    "\n",
    "    return training_state\n",
    "\n",
    "\n",
    "def print_model_metadata(training_state):\n",
    "    header = f'\\n{\"*\"*5} Model training metadata: {\"*\"*5}'\n",
    "    print(header)\n",
    "\n",
    "    for key, value in training_state.items():\n",
    "        if key != 'state_dict':  # don't print state_dict it's a bunch of numbers...\n",
    "            print(f'{key}: {value}')\n",
    "    print(f'{\"*\" * len(header)}\\n')\n",
    "    \n",
    "\n",
    "# This one makes sure we don't overwrite the valuable model binaries (feel free to ignore - not crucial to GAT method)\n",
    "def get_available_binary_name(dataset_name='unknown'):\n",
    "    prefix = f'gat_{dataset_name}'\n",
    "\n",
    "    def valid_binary_name(binary_name):\n",
    "        # First time you see raw f-string? Don't worry the only trick is to double the brackets.\n",
    "        pattern = re.compile(rf'{prefix}_[0-9]{{6}}\\.pth')\n",
    "        return re.fullmatch(pattern, binary_name) is not None\n",
    "\n",
    "    # Just list the existing binaries so that we don't overwrite them but write to a new one\n",
    "    valid_binary_names = list(filter(valid_binary_name, os.listdir(BINARIES_PATH)))\n",
    "    if len(valid_binary_names) > 0:\n",
    "        last_binary_name = sorted(valid_binary_names)[-1]\n",
    "        new_suffix = int(last_binary_name.split('.')[0][-6:]) + 1  # increment by 1\n",
    "        return f'{prefix}_{str(new_suffix).zfill(6)}.pth'\n",
    "    else:\n",
    "        return f'{prefix}_000000.pth'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "binary-brother",
   "metadata": {},
   "source": [
    "Nice, now `argparse` is just a nice way to **organize** your program settings:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "numerous-planet",
   "metadata": {},
   "outputs": [],
   "source": [
    "import argparse\n",
    "\n",
    "\n",
    "def get_training_args():\n",
    "    parser = argparse.ArgumentParser()\n",
    "\n",
    "    # Training related\n",
    "    parser.add_argument(\"--num_of_epochs\", type=int, help=\"number of training epochs\", default=200)\n",
    "    parser.add_argument(\"--patience_period\", type=int, help=\"number of epochs with no improvement on val before terminating\", default=100)\n",
    "    parser.add_argument(\"--lr\", type=float, help=\"model learning rate\", default=5e-3)\n",
    "    parser.add_argument(\"--weight_decay\", type=float, help=\"L2 regularization on model weights\", default=0)\n",
    "    parser.add_argument(\"--should_test\", type=bool, help='should test the model on the test dataset?', default=True)\n",
    "    parser.add_argument(\"--force_cpu\", type=bool, help='use CPU if your GPU is too small', default=False)\n",
    "\n",
    "    # Dataset related (note: we need the dataset name for metadata and related stuff, and not for picking the dataset)\n",
    "    parser.add_argument(\"--dataset_name\", choices=[el.name for el in DatasetType], help='dataset to use for training', default=DatasetType.PPI.name)\n",
    "    parser.add_argument(\"--batch_size\", type=int, help='number of graphs in a batch', default=2)\n",
    "    parser.add_argument(\"--should_visualize\", type=bool, help='should visualize the dataset?', default=False)\n",
    "\n",
    "    # Logging/debugging/checkpoint related (helps a lot with experimentation)\n",
    "    parser.add_argument(\"--enable_tensorboard\", type=bool, help=\"enable tensorboard logging\", default=False)\n",
    "    parser.add_argument(\"--console_log_freq\", type=int, help=\"log to output console (epoch) freq (None for no logging)\", default=10)\n",
    "    parser.add_argument(\"--checkpoint_freq\", type=int, help=\"checkpoint model saving (epoch) freq (None for no logging)\", default=5)\n",
    "    args = parser.parse_args('')\n",
    "\n",
    "    # I'm leaving the hyperparam values as reported in the paper, but I experimented a bit and the comments suggest\n",
    "    # how you can make GAT achieve an even higher micro-F1 or make it smaller\n",
    "    gat_config = {\n",
    "        # GNNs, contrary to CNNs, are often shallow (it ultimately depends on the graph properties)\n",
    "        \"num_of_layers\": 3,  # PPI has got 42% of nodes with all 0 features - that's why 3 layers are useful\n",
    "        \"num_heads_per_layer\": [4, 4, 6],  # other values may give even better results from the reported ones\n",
    "        \"num_features_per_layer\": [PPI_NUM_INPUT_FEATURES, 64, 64, PPI_NUM_CLASSES],  # 64 would also give ~0.975 uF1!\n",
    "        \"add_skip_connection\": True,  # skip connection is very important! (keep it otherwise micro-F1 is almost 0)\n",
    "        \"bias\": True,  # bias doesn't matter that much\n",
    "        \"dropout\": 0.0,  # dropout hurts the performance (best to keep it at 0)\n",
    "    }\n",
    "\n",
    "    # Wrapping training configuration into a dictionary\n",
    "    training_config = dict()\n",
    "    for arg in vars(args):\n",
    "        training_config[arg] = getattr(args, arg)\n",
    "    training_config['ppi_load_test_only'] = False  # load both train/val/test data loaders (don't change it)\n",
    "\n",
    "    # Add additional config information\n",
    "    training_config.update(gat_config)\n",
    "\n",
    "    return training_config"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "waiting-stanford",
   "metadata": {},
   "source": [
    "Now for the juicy part. 🍪🎅\n",
    "\n",
    "Here, we organize, high-level, everything we need for training GAT. Just combining the components we already learned."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "apparent-precipitation",
   "metadata": {},
   "outputs": [],
   "source": [
    "import time\n",
    "\n",
    "\n",
    "def train_gat_ppi(config):\n",
    "    \"\"\"\n",
    "    Very similar to Cora's training script. The main differences are:\n",
    "    1. Using dataloaders since we're dealing with an inductive setting - multiple graphs per batch\n",
    "    2. Doing multi-class classification (BCEWithLogitsLoss) and reporting micro-F1 instead of accuracy\n",
    "    3. Model architecture and hyperparams are a bit different (as reported in the GAT paper)\n",
    "\n",
    "    \"\"\"\n",
    "    global BEST_VAL_MICRO_F1, BEST_VAL_LOSS\n",
    "\n",
    "    # Checking whether you have a strong GPU. Since PPI training requires almost 8 GBs of VRAM\n",
    "    # I've added the option to force the use of CPU even though you have a GPU on your system (but it's too weak).\n",
    "    device = torch.device(\"cuda\" if torch.cuda.is_available() and not config['force_cpu'] else \"cpu\")\n",
    "\n",
    "    # Step 1: prepare the data loaders\n",
    "    data_loader_train, data_loader_val, data_loader_test = load_graph_data(config, device)\n",
    "\n",
    "    # Step 2: prepare the model\n",
    "    gat = GAT(\n",
    "        num_of_layers=config['num_of_layers'],\n",
    "        num_heads_per_layer=config['num_heads_per_layer'],\n",
    "        num_features_per_layer=config['num_features_per_layer'],\n",
    "        add_skip_connection=config['add_skip_connection'],\n",
    "        bias=config['bias'],\n",
    "        dropout=config['dropout'],\n",
    "        log_attention_weights=False  # no need to store attentions, used only in playground.py for visualizations\n",
    "    ).to(device)\n",
    "\n",
    "    # Step 3: Prepare other training related utilities (loss & optimizer and decorator function)\n",
    "    loss_fn = nn.BCEWithLogitsLoss(reduction='mean')\n",
    "    optimizer = Adam(gat.parameters(), lr=config['lr'], weight_decay=config['weight_decay'])\n",
    "\n",
    "    # The decorator function makes things cleaner since there is a lot of redundancy between the train and val loops\n",
    "    main_loop = get_main_loop(\n",
    "        config,\n",
    "        gat,\n",
    "        loss_fn,\n",
    "        optimizer,\n",
    "        config['patience_period'],\n",
    "        time.time())\n",
    "\n",
    "    BEST_VAL_MICRO_F1, BEST_VAL_LOSS, PATIENCE_CNT = [0, 0, 0]  # reset vars used for early stopping\n",
    "\n",
    "    # Step 4: Start the training procedure\n",
    "    for epoch in range(config['num_of_epochs']):\n",
    "        # Training loop\n",
    "        main_loop(phase=LoopPhase.TRAIN, data_loader=data_loader_train, epoch=epoch)\n",
    "\n",
    "        # Validation loop\n",
    "        with torch.no_grad():\n",
    "            try:\n",
    "                main_loop(phase=LoopPhase.VAL, data_loader=data_loader_val, epoch=epoch)\n",
    "            except Exception as e:  # \"patience has run out\" exception :O\n",
    "                print(str(e))\n",
    "                break  # break out from the training loop\n",
    "\n",
    "    # Step 5: Potentially test your model\n",
    "    # Don't overfit to the test dataset - only when you've fine-tuned your model on the validation dataset should you\n",
    "    # report your final loss and micro-F1 on the test dataset. Friends don't let friends overfit to the test data. <3\n",
    "    if config['should_test']:\n",
    "        micro_f1 = main_loop(phase=LoopPhase.TEST, data_loader=data_loader_test)\n",
    "        config['test_perf'] = micro_f1\n",
    "\n",
    "        print('*' * 50)\n",
    "        print(f'Test micro-F1 = {micro_f1}')\n",
    "    else:\n",
    "        config['test_perf'] = -1\n",
    "\n",
    "    # Save the latest GAT in the binaries directory\n",
    "    torch.save(\n",
    "        get_training_state(config, gat),\n",
    "        os.path.join(BINARIES_PATH, get_available_binary_name(config['dataset_name']))\n",
    "    )"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "western-grenada",
   "metadata": {},
   "source": [
    "🎉🎉🎉 \n",
    "\n",
    "Now for the core part of the training - the main loop, as I've dubbed it. \n",
    "\n",
    "I've organized it like this so that I don't have to copy/paste bunch of the same code for train/val/test loops.\n",
    "\n",
    "**Friends don't let friends copy/paste (unless it's from the Stack Overflow)**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "comparative-heart",
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.metrics import f1_score\n",
    "\n",
    "\n",
    "# Simple decorator function so that I don't have to pass arguments that don't change from epoch to epoch\n",
    "def get_main_loop(config, gat, sigmoid_cross_entropy_loss, optimizer, patience_period, time_start):\n",
    "\n",
    "    device = next(gat.parameters()).device  # fetch the device info from the model instead of passing it as a param\n",
    "\n",
    "    def main_loop(phase, data_loader, epoch=0):\n",
    "        global BEST_VAL_MICRO_F1, BEST_VAL_LOSS, PATIENCE_CNT, writer\n",
    "\n",
    "        # Certain modules behave differently depending on whether we're training the model or not.\n",
    "        # e.g. nn.Dropout - we only want to drop model weights during the training.\n",
    "        if phase == LoopPhase.TRAIN:\n",
    "            gat.train()\n",
    "        else:\n",
    "            gat.eval()\n",
    "\n",
    "        # Iterate over batches of graph data (2 graphs per batch was used in the original paper for the PPI dataset)\n",
    "        # We merge them into a single graph with 2 connected components, that's the main idea. After that\n",
    "        # the implementation #3 is agnostic to the fact that those are multiple and not a single graph!\n",
    "        for batch_idx, (node_features, gt_node_labels, edge_index) in enumerate(data_loader):\n",
    "            # Push the batch onto GPU - note PPI is to big to load the whole dataset into a normal GPU\n",
    "            # it takes almost 8 GBs of VRAM to train it on a GPU\n",
    "            edge_index = edge_index.to(device)\n",
    "            node_features = node_features.to(device)\n",
    "            gt_node_labels = gt_node_labels.to(device)\n",
    "\n",
    "            # I pack data into tuples because GAT uses nn.Sequential which expects this format\n",
    "            graph_data = (node_features, edge_index)\n",
    "\n",
    "            # Note: [0] just extracts the node_features part of the data (index 1 contains the edge_index)\n",
    "            # shape = (N, C) where N is the number of nodes in the batch and C is the number of classes (121 for PPI)\n",
    "            # GAT imp #3 is agnostic to the fact that we actually have multiple graphs\n",
    "            # (it sees a single graph with multiple connected components)\n",
    "            nodes_unnormalized_scores = gat(graph_data)[0]\n",
    "\n",
    "            # Example: because PPI has 121 labels let's make a simple toy example that will show how the loss works.\n",
    "            # Let's say we have 3 labels instead and a single node's unnormalized (raw GAT output) scores are [-3, 0, 3]\n",
    "            # What this loss will do is first it will apply a sigmoid and so we'll end up with: [0.048, 0.5, 0.95]\n",
    "            # next it will apply a binary cross entropy across all of these and find the average, and that's it!\n",
    "            # So if the true classes were [0, 0, 1] the loss would be (-log(1-0.048) + -log(1-0.5) + -log(0.95))/3.\n",
    "            # You can see that the logarithm takes 2 forms depending on whether the true label is 0 or 1,\n",
    "            # either -log(1-x) or -log(x) respectively. Easy-peasy. <3\n",
    "            loss = sigmoid_cross_entropy_loss(nodes_unnormalized_scores, gt_node_labels)\n",
    "\n",
    "            if phase == LoopPhase.TRAIN:\n",
    "                optimizer.zero_grad()  # clean the trainable weights gradients in the computational graph (.grad fields)\n",
    "                loss.backward()  # compute the gradients for every trainable weight in the computational graph\n",
    "                optimizer.step()  # apply the gradients to weights\n",
    "\n",
    "            # Calculate the main metric - micro F1, check out this link for what micro-F1 exactly is:\n",
    "            # https://www.kaggle.com/enforcer007/what-is-micro-averaged-f1-score\n",
    "\n",
    "            # Convert unnormalized scores into predictions. Explanation:\n",
    "            # If the unnormalized score is bigger than 0 that means that sigmoid would have a value higher than 0.5\n",
    "            # (by sigmoid's definition) and thus we have predicted 1 for that label otherwise we have predicted 0.\n",
    "            pred = (nodes_unnormalized_scores > 0).float().cpu().numpy()\n",
    "            gt = gt_node_labels.cpu().numpy()\n",
    "            micro_f1 = f1_score(gt, pred, average='micro')\n",
    "\n",
    "            #\n",
    "            # Logging\n",
    "            #\n",
    "\n",
    "            global_step = len(data_loader) * epoch + batch_idx\n",
    "            if phase == LoopPhase.TRAIN:\n",
    "                # Log metrics\n",
    "                if config['enable_tensorboard']:\n",
    "                    writer.add_scalar('training_loss', loss.item(), global_step)\n",
    "                    writer.add_scalar('training_micro_f1', micro_f1, global_step)\n",
    "\n",
    "                # Log to console\n",
    "                if config['console_log_freq'] is not None and epoch % config['console_log_freq'] == 0 and batch_idx == 0:\n",
    "                    print(f'GAT training: time elapsed= {(time.time() - time_start):.2f} [s] |'\n",
    "                          f' epoch={epoch + 1} | batch={batch_idx + 1} | train micro-F1={micro_f1}.')\n",
    "\n",
    "                # Save model checkpoint\n",
    "                if config['checkpoint_freq'] is not None and (epoch + 1) % config['checkpoint_freq'] == 0 and batch_idx == 0:\n",
    "                    ckpt_model_name = f'gat_{config[\"dataset_name\"]}_ckpt_epoch_{epoch + 1}.pth'\n",
    "                    config['test_perf'] = -1  # test perf not calculated yet, note: perf means main metric micro-F1 here\n",
    "                    torch.save(get_training_state(config, gat), os.path.join(CHECKPOINTS_PATH, ckpt_model_name))\n",
    "\n",
    "            elif phase == LoopPhase.VAL:\n",
    "                # Log metrics\n",
    "                if config['enable_tensorboard']:\n",
    "                    writer.add_scalar('val_loss', loss.item(), global_step)\n",
    "                    writer.add_scalar('val_micro_f1', micro_f1, global_step)\n",
    "\n",
    "                # Log to console\n",
    "                if config['console_log_freq'] is not None and epoch % config['console_log_freq'] == 0 and batch_idx == 0:\n",
    "                    print(f'GAT validation: time elapsed= {(time.time() - time_start):.2f} [s] |'\n",
    "                          f' epoch={epoch + 1} | batch={batch_idx + 1} | val micro-F1={micro_f1}')\n",
    "\n",
    "                # The \"patience\" logic - should we break out from the training loop? If either validation micro-F1\n",
    "                # keeps going up or the val loss keeps going down we won't stop\n",
    "                if micro_f1 > BEST_VAL_MICRO_F1 or loss.item() < BEST_VAL_LOSS:\n",
    "                    BEST_VAL_MICRO_F1 = max(micro_f1, BEST_VAL_MICRO_F1)  # keep track of the best validation micro_f1 so far\n",
    "                    BEST_VAL_LOSS = min(loss.item(), BEST_VAL_LOSS)  # and the minimal loss\n",
    "                    PATIENCE_CNT = 0  # reset the counter every time we encounter new best micro_f1\n",
    "                else:\n",
    "                    PATIENCE_CNT += 1  # otherwise keep counting\n",
    "\n",
    "                if PATIENCE_CNT >= patience_period:\n",
    "                    raise Exception('Stopping the training, the universe has no more patience for this training.')\n",
    "\n",
    "            else:\n",
    "                return micro_f1  # in the case of test phase we just report back the test micro_f1\n",
    "\n",
    "    return main_loop  # return the decorated function"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "numerical-stupid",
   "metadata": {},
   "source": [
    "That was all we needed! Let's train it! 💪💪💪\n",
    "\n",
    "Keep in mind that PPI training takes much more time than training on Cora.\n",
    "\n",
    "Additionally you'll need 8+ GBs GPU if you want to train it on your GPU. Alternatively you can set the `--force_cpu` flag in the `get_training_args` function to train it on your CPU or simply use the pre-checked-in model I provided. The following section, part 4, doesn't depend on this so you can skip it if you want."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "local-details",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Loading train graph 1 to CPU. It has 1767 nodes and 34085 edges.\n",
      "Loading train graph 2 to CPU. It has 1377 nodes and 31081 edges.\n",
      "Loading train graph 3 to CPU. It has 2263 nodes and 61907 edges.\n",
      "Loading train graph 4 to CPU. It has 2339 nodes and 67769 edges.\n",
      "Loading train graph 5 to CPU. It has 1578 nodes and 37740 edges.\n",
      "Loading train graph 6 to CPU. It has 1021 nodes and 19237 edges.\n",
      "Loading train graph 7 to CPU. It has 1823 nodes and 46153 edges.\n",
      "Loading train graph 8 to CPU. It has 2488 nodes and 72878 edges.\n",
      "Loading train graph 9 to CPU. It has 591 nodes and 8299 edges.\n",
      "Loading train graph 10 to CPU. It has 3312 nodes and 109510 edges.\n",
      "Loading train graph 11 to CPU. It has 2401 nodes and 66619 edges.\n",
      "Loading train graph 12 to CPU. It has 1878 nodes and 48146 edges.\n",
      "Loading train graph 13 to CPU. It has 1819 nodes and 47587 edges.\n",
      "Loading train graph 14 to CPU. It has 3480 nodes and 110234 edges.\n",
      "Loading train graph 15 to CPU. It has 2794 nodes and 88112 edges.\n",
      "Loading train graph 16 to CPU. It has 2326 nodes and 62188 edges.\n",
      "Loading train graph 17 to CPU. It has 2650 nodes and 79714 edges.\n",
      "Loading train graph 18 to CPU. It has 2815 nodes and 88335 edges.\n",
      "Loading train graph 19 to CPU. It has 3163 nodes and 97321 edges.\n",
      "Loading train graph 20 to CPU. It has 3021 nodes and 94359 edges.\n",
      "Loading valid graph 21 to CPU. It has 3230 nodes and 100676 edges.\n",
      "Loading valid graph 22 to CPU. It has 3284 nodes and 104758 edges.\n",
      "Loading test graph 23 to CPU. It has 3224 nodes and 103872 edges.\n",
      "Loading test graph 24 to CPU. It has 2300 nodes and 63628 edges.\n",
      "GAT training: time elapsed= 0.47 [s] | epoch=1 | batch=1 | train micro-F1=0.39645115585841423.\n",
      "GAT validation: time elapsed= 6.49 [s] | epoch=1 | batch=1 | val micro-F1=0.46894684585460605\n",
      "GAT training: time elapsed= 68.01 [s] | epoch=11 | batch=1 | train micro-F1=0.7495442966809728.\n",
      "GAT validation: time elapsed= 74.10 [s] | epoch=11 | batch=1 | val micro-F1=0.7429640455443971\n",
      "GAT training: time elapsed= 135.39 [s] | epoch=21 | batch=1 | train micro-F1=0.9056125615159257.\n",
      "GAT validation: time elapsed= 141.38 [s] | epoch=21 | batch=1 | val micro-F1=0.8692865985387286\n",
      "GAT training: time elapsed= 174.59 [s] | epoch=31 | batch=1 | train micro-F1=0.852734024517641.\n",
      "GAT validation: time elapsed= 176.07 [s] | epoch=31 | batch=1 | val micro-F1=0.8510402882091819\n",
      "GAT training: time elapsed= 193.13 [s] | epoch=41 | batch=1 | train micro-F1=0.9091961098116459.\n",
      "GAT validation: time elapsed= 194.86 [s] | epoch=41 | batch=1 | val micro-F1=0.8957993641635122\n",
      "GAT training: time elapsed= 212.21 [s] | epoch=51 | batch=1 | train micro-F1=0.9549158379804963.\n",
      "GAT validation: time elapsed= 214.07 [s] | epoch=51 | batch=1 | val micro-F1=0.9105656595092702\n",
      "GAT training: time elapsed= 232.83 [s] | epoch=61 | batch=1 | train micro-F1=0.9411274572241328.\n",
      "GAT validation: time elapsed= 234.51 [s] | epoch=61 | batch=1 | val micro-F1=0.9256247460160015\n",
      "GAT training: time elapsed= 251.93 [s] | epoch=71 | batch=1 | train micro-F1=0.9562771800594922.\n",
      "GAT validation: time elapsed= 253.68 [s] | epoch=71 | batch=1 | val micro-F1=0.9323508937273088\n",
      "GAT training: time elapsed= 271.17 [s] | epoch=81 | batch=1 | train micro-F1=0.9628272424152389.\n",
      "GAT validation: time elapsed= 272.83 [s] | epoch=81 | batch=1 | val micro-F1=0.9301051928535647\n",
      "GAT training: time elapsed= 290.20 [s] | epoch=91 | batch=1 | train micro-F1=0.9638334753356641.\n",
      "GAT validation: time elapsed= 291.98 [s] | epoch=91 | batch=1 | val micro-F1=0.9433430464495091\n",
      "GAT training: time elapsed= 309.38 [s] | epoch=101 | batch=1 | train micro-F1=0.9482550743874786.\n",
      "GAT validation: time elapsed= 311.11 [s] | epoch=101 | batch=1 | val micro-F1=0.9437710780040279\n",
      "GAT training: time elapsed= 329.08 [s] | epoch=111 | batch=1 | train micro-F1=0.9736105174083347.\n",
      "GAT validation: time elapsed= 330.86 [s] | epoch=111 | batch=1 | val micro-F1=0.9487691831428638\n",
      "GAT training: time elapsed= 348.27 [s] | epoch=121 | batch=1 | train micro-F1=0.975528246668577.\n",
      "GAT validation: time elapsed= 350.01 [s] | epoch=121 | batch=1 | val micro-F1=0.9547567447026398\n",
      "GAT training: time elapsed= 367.43 [s] | epoch=131 | batch=1 | train micro-F1=0.9862278019385557.\n",
      "GAT validation: time elapsed= 369.18 [s] | epoch=131 | batch=1 | val micro-F1=0.9565031097030525\n",
      "GAT training: time elapsed= 386.62 [s] | epoch=141 | batch=1 | train micro-F1=0.9718210049457011.\n",
      "GAT validation: time elapsed= 388.38 [s] | epoch=141 | batch=1 | val micro-F1=0.9539446028389031\n",
      "GAT training: time elapsed= 405.88 [s] | epoch=151 | batch=1 | train micro-F1=0.9759749723314961.\n",
      "GAT validation: time elapsed= 407.56 [s] | epoch=151 | batch=1 | val micro-F1=0.9554913006414163\n",
      "GAT training: time elapsed= 424.99 [s] | epoch=161 | batch=1 | train micro-F1=0.9731688518900793.\n",
      "GAT validation: time elapsed= 426.79 [s] | epoch=161 | batch=1 | val micro-F1=0.954294638658435\n",
      "GAT training: time elapsed= 444.32 [s] | epoch=171 | batch=1 | train micro-F1=0.9865455171994405.\n",
      "GAT validation: time elapsed= 445.98 [s] | epoch=171 | batch=1 | val micro-F1=0.9597544071574757\n",
      "GAT training: time elapsed= 463.37 [s] | epoch=181 | batch=1 | train micro-F1=0.9920675718520388.\n",
      "GAT validation: time elapsed= 465.07 [s] | epoch=181 | batch=1 | val micro-F1=0.9597552723704509\n",
      "GAT training: time elapsed= 482.68 [s] | epoch=191 | batch=1 | train micro-F1=0.9852743743022286.\n",
      "GAT validation: time elapsed= 484.36 [s] | epoch=191 | batch=1 | val micro-F1=0.9605410961577663\n",
      "**************************************************\n",
      "Test micro-F1 = 0.9786080446580244\n"
     ]
    }
   ],
   "source": [
    "# Train the graph attention network (GAT)\n",
    "train_gat_ppi(get_training_args())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "roman-weather",
   "metadata": {},
   "source": [
    "Nice!!! 🎉🎉🎉 Level 4 unlocked (GAT visualizations 🔮). 😍\n",
    "\n",
    "We just achieved `0.978` micro-F1 on PPI's test graphs! The same numbers as reported in the original GAT paper!\n",
    "\n",
    "So we now have everything in place:\n",
    "1. PPI data loading and visualizations 📜 -> checked\n",
    "2. GAT model defined 🦄 -> checked\n",
    "3. Training loop setup and the trained model binaries 💪 -> checked\n",
    "\n",
    "Now let's play the GAT model under a microscope 🔬🔬🔬 and understand the weights we got - we can do that in many ways."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "constitutional-reach",
   "metadata": {},
   "source": [
    "# Part 4: Visualizing GAT on PPI 🔮\n",
    "\n",
    "I tried visualizing PPI's 2D embeddings using t-SNE without any label/color information but it's not that informative, so we'll only do **attention** and **entropy visualizations** in this notebook.\n",
    "\n",
    "Let's start by defining some functions we'll need. \n",
    "\n",
    "The following cell's code snippet will get called multiple times so let's just extract it inside a function - a nice modular design.\n",
    "\n",
    "*Note: the main reason is actually that igraph is having problems with Jupyter so I'm working around it, check out the [original code](https://github.com/gordicaleksa/pytorch-GAT/blob/main/playground.py#L147) if you're curious* 😂"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "satisfactory-yeast",
   "metadata": {},
   "outputs": [],
   "source": [
    "def gat_forward_pass(model_name, dataset_name):\n",
    "    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")  # checking whether you have a GPU, I hope so!\n",
    "\n",
    "    config = {\n",
    "        'dataset_name': dataset_name,\n",
    "        'should_visualize': False,  # don't visualize the dataset\n",
    "        'batch_size': 2,  # we're using 2 graphs per batch as reported in the paper\n",
    "        'ppi_load_test_only': True  # optimization, we're loading only test graphs\n",
    "    }\n",
    "\n",
    "    # Step 1: Prepare the data\n",
    "    data_loader_test = load_graph_data(config, device)\n",
    "    node_features, node_labels, topology = next(iter(data_loader_test))\n",
    "    node_features = node_features.to(device)  # need to explicitly push them to GPU since PPI eats up a lot of VRAM\n",
    "    node_labels = node_labels.to(device)\n",
    "    topology = topology.to(device)\n",
    "\n",
    "    # Step 2: Prepare the model\n",
    "    model_path = os.path.join(BINARIES_PATH, model_name)\n",
    "    model_state = torch.load(model_path)\n",
    "\n",
    "    gat = GAT(\n",
    "        num_of_layers=model_state['num_of_layers'],\n",
    "        num_heads_per_layer=model_state['num_heads_per_layer'],\n",
    "        num_features_per_layer=model_state['num_features_per_layer'],\n",
    "        add_skip_connection=model_state['add_skip_connection'],\n",
    "        bias=model_state['bias'],\n",
    "        dropout=model_state['dropout'],\n",
    "        log_attention_weights=True\n",
    "    ).to(device)\n",
    "\n",
    "    print_model_metadata(model_state)\n",
    "    assert model_state['dataset_name'].lower() == dataset_name.lower(), \\\n",
    "        f\"The model was trained on {model_state['dataset_name']} but you're calling it on {dataset_name}.\"\n",
    "    gat.load_state_dict(model_state[\"state_dict\"], strict=True)\n",
    "    gat.eval()  # some layers like nn.Dropout behave differently in train vs eval mode so this part is important\n",
    "\n",
    "    # Step 3: Calculate the things we'll need for different visualization types (attention, scores, edge_index)\n",
    "\n",
    "    # This context manager is important (and you'll often see it), otherwise PyTorch will eat much more memory.\n",
    "    # It would be saving activations for backprop but we are not going to do any model training just the prediction.\n",
    "    with torch.no_grad():\n",
    "        # Step 3: Run predictions and collect the high dimensional data\n",
    "        all_nodes_unnormalized_scores, _ = gat((node_features, topology))  # shape = (N, num of classes)\n",
    "        all_nodes_unnormalized_scores = all_nodes_unnormalized_scores.cpu().numpy()\n",
    "\n",
    "    return all_nodes_unnormalized_scores, topology, node_labels, gat"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "extensive-drain",
   "metadata": {},
   "source": [
    "Nice that one just produces the data that'll get consumed in the downstream visualizations that you'll see defined in the following cells."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "impressed-tamil",
   "metadata": {},
   "source": [
    "# Visualizing neighborhood attention 📣\n",
    "\n",
    "So, you now hopefully understand how GAT roughly works, and so you know that during the aggregation stage every single node assigns an **attention coefficient** to every single one of its neighbors (including itself since we added self edges).\n",
    "\n",
    "Any ideas on what we could visualize? Well let's pick some nodes and see which attention patterns they've learned!\n",
    "\n",
    "The first idea that may pop to your mind is to draw edges **thicker** if the **attention is larger** and vice versa (*well that's also the last idea that pops to my mind*).\n",
    "\n",
    "Let's do it!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "combined-ceiling",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Loading test graph 23 to CPU. It has 3224 nodes and 103872 edges.\n",
      "Loading test graph 24 to CPU. It has 2300 nodes and 63628 edges.\n",
      "\n",
      "***** Model training metadata: *****\n",
      "commit_hash: ad028f5d4bdb533020a73c6ffcacfb2fd9767539\n",
      "dataset_name: PPI\n",
      "num_of_epochs: 200\n",
      "test_perf: 0.9779919847816116\n",
      "num_of_layers: 3\n",
      "num_heads_per_layer: [4, 4, 6]\n",
      "num_features_per_layer: [50, 256, 256, 121]\n",
      "add_skip_connection: True\n",
      "bias: True\n",
      "dropout: 0.0\n",
      "layer_type: IMP3\n",
      "*************************************\n",
      "\n",
      "tensor([False, False, False,  ..., False, False, False], device='cuda:0')\n",
      "Max attention weight = 0.7059098482131958 and min = 0.0023552586790174246\n"
     ]
    },
    {
     "data": {
      "image/svg+xml": [
       "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
       "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"600pt\" height=\"600pt\" viewBox=\"0 0 600 600\" version=\"1.1\">\n",
       "<g id=\"surface14\">\n",
       "<rect x=\"0\" y=\"0\" width=\"600\" height=\"600\" style=\"fill:rgb(100%,100%,100%);fill-opacity:1;stroke:none;\"/>\n",
       "<path style=\"fill:none;stroke-width:0.00769083;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(26.666667%,26.666667%,26.666667%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 323.871094 289.585938 C 323.871094 297.871094 317.15625 304.585938 308.871094 304.585938 C 300.589844 304.585938 293.871094 297.871094 293.871094 289.585938 C 293.871094 281.300781 300.589844 274.585938 308.871094 274.585938 C 317.15625 274.585938 323.871094 281.300781 323.871094 289.585938 \"/>\n",
       "<path style=\"fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(26.666667%,26.666667%,26.666667%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 298.265625 300.191406 L 580 300.191406 \"/>\n",
       "<path style=\"fill:none;stroke-width:0.00616214;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(26.666667%,26.666667%,26.666667%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 298.265625 300.191406 L 564.652344 391.636719 \"/>\n",
       "<path style=\"fill:none;stroke-width:0.0187682;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(26.666667%,26.666667%,26.666667%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 298.265625 300.191406 L 520.273438 473.117188 \"/>\n",
       "<path style=\"fill:none;stroke-width:0.00333649;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(26.666667%,26.666667%,26.666667%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 298.265625 300.191406 L 451.710938 535.753906 \"/>\n",
       "<path style=\"fill:none;stroke-width:0.00675488;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(26.666667%,26.666667%,26.666667%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 298.265625 300.191406 L 366.421875 572.726563 \"/>\n",
       "<path style=\"fill:none;stroke-width:0.0154947;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(26.666667%,26.666667%,26.666667%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 298.265625 300.191406 L 273.710938 580 \"/>\n",
       "<path style=\"fill:none;stroke-width:0.00675488;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(26.666667%,26.666667%,26.666667%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 298.265625 300.191406 L 183.675781 556.785156 \"/>\n",
       "<path style=\"fill:none;stroke-width:0.00361774;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(26.666667%,26.666667%,26.666667%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 298.265625 300.191406 L 106.125 505.613281 \"/>\n",
       "<path style=\"fill:none;stroke-width:0.00793732;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(26.666667%,26.666667%,26.666667%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 298.265625 300.191406 L 49.507813 432.054688 \"/>\n",
       "<path style=\"fill:none;stroke-width:0.00784118;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(26.666667%,26.666667%,26.666667%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 298.265625 300.191406 L 20 344.132813 \"/>\n",
       "<path style=\"fill:none;stroke-width:0.00784118;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(26.666667%,26.666667%,26.666667%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 298.265625 300.191406 L 20.8125 251.417969 \"/>\n",
       "<path style=\"fill:none;stroke-width:0.00624381;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(26.666667%,26.666667%,26.666667%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 298.265625 300.191406 L 51.855469 164.019531 \"/>\n",
       "<path style=\"fill:none;stroke-width:0.271425;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(26.666667%,26.666667%,26.666667%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 298.265625 300.191406 L 109.75 91.460938 \"/>\n",
       "<path style=\"fill:none;stroke-width:0.00784118;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(26.666667%,26.666667%,26.666667%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 298.265625 300.191406 L 188.183594 41.644531 \"/>\n",
       "<path style=\"fill:none;stroke-width:0.00748776;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(26.666667%,26.666667%,26.666667%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 298.265625 300.191406 L 278.613281 20 \"/>\n",
       "<path style=\"fill:none;stroke-width:0.00784118;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(26.666667%,26.666667%,26.666667%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 298.265625 300.191406 L 371.183594 28.886719 \"/>\n",
       "<path style=\"fill:none;stroke-width:0.00675488;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(26.666667%,26.666667%,26.666667%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 298.265625 300.191406 L 455.808594 67.335938 \"/>\n",
       "<path style=\"fill:none;stroke-width:0.00784118;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(26.666667%,26.666667%,26.666667%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 298.265625 300.191406 L 523.269531 131.15625 \"/>\n",
       "<path style=\"fill:none;stroke-width:0.00897365;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(26.666667%,26.666667%,26.666667%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 298.265625 300.191406 L 566.210938 213.398438 \"/>\n",
       "<path style=\"fill-rule:nonzero;fill:rgb(100%,0%,0%);fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 308.265625 300.191406 C 308.265625 305.714844 303.789063 310.191406 298.265625 310.191406 C 292.742188 310.191406 288.265625 305.714844 288.265625 300.191406 C 288.265625 294.667969 292.742188 290.191406 298.265625 290.191406 C 303.789063 290.191406 308.265625 294.667969 308.265625 300.191406 \"/>\n",
       "<path style=\"fill-rule:nonzero;fill:rgb(100%,0%,0%);fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 590 300.191406 C 590 305.714844 585.523438 310.191406 580 310.191406 C 574.476563 310.191406 570 305.714844 570 300.191406 C 570 294.667969 574.476563 290.191406 580 290.191406 C 585.523438 290.191406 590 294.667969 590 300.191406 \"/>\n",
       "<path style=\"fill-rule:nonzero;fill:rgb(100%,0%,0%);fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 574.652344 391.636719 C 574.652344 397.160156 570.171875 401.636719 564.652344 401.636719 C 559.128906 401.636719 554.652344 397.160156 554.652344 391.636719 C 554.652344 386.113281 559.128906 381.636719 564.652344 381.636719 C 570.171875 381.636719 574.652344 386.113281 574.652344 391.636719 \"/>\n",
       "<path style=\"fill-rule:nonzero;fill:rgb(100%,0%,0%);fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 530.273438 473.117188 C 530.273438 478.640625 525.796875 483.117188 520.273438 483.117188 C 514.753906 483.117188 510.273438 478.640625 510.273438 473.117188 C 510.273438 467.59375 514.753906 463.117188 520.273438 463.117188 C 525.796875 463.117188 530.273438 467.59375 530.273438 473.117188 \"/>\n",
       "<path style=\"fill-rule:nonzero;fill:rgb(100%,0%,0%);fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 461.710938 535.753906 C 461.710938 541.277344 457.230469 545.753906 451.710938 545.753906 C 446.1875 545.753906 441.710938 541.277344 441.710938 535.753906 C 441.710938 530.230469 446.1875 525.753906 451.710938 525.753906 C 457.230469 525.753906 461.710938 530.230469 461.710938 535.753906 \"/>\n",
       "<path style=\"fill-rule:nonzero;fill:rgb(100%,0%,0%);fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 376.421875 572.726563 C 376.421875 578.25 371.945313 582.726563 366.421875 582.726563 C 360.902344 582.726563 356.421875 578.25 356.421875 572.726563 C 356.421875 567.203125 360.902344 562.726563 366.421875 562.726563 C 371.945313 562.726563 376.421875 567.203125 376.421875 572.726563 \"/>\n",
       "<path style=\"fill-rule:nonzero;fill:rgb(100%,0%,0%);fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 283.710938 580 C 283.710938 585.523438 279.234375 590 273.710938 590 C 268.1875 590 263.710938 585.523438 263.710938 580 C 263.710938 574.476563 268.1875 570 273.710938 570 C 279.234375 570 283.710938 574.476563 283.710938 580 \"/>\n",
       "<path style=\"fill-rule:nonzero;fill:rgb(100%,0%,0%);fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 193.675781 556.785156 C 193.675781 562.308594 189.195313 566.785156 183.675781 566.785156 C 178.152344 566.785156 173.675781 562.308594 173.675781 556.785156 C 173.675781 551.261719 178.152344 546.785156 183.675781 546.785156 C 189.195313 546.785156 193.675781 551.261719 193.675781 556.785156 \"/>\n",
       "<path style=\"fill-rule:nonzero;fill:rgb(100%,0%,0%);fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 116.125 505.613281 C 116.125 511.136719 111.644531 515.613281 106.125 515.613281 C 100.601563 515.613281 96.125 511.136719 96.125 505.613281 C 96.125 500.089844 100.601563 495.613281 106.125 495.613281 C 111.644531 495.613281 116.125 500.089844 116.125 505.613281 \"/>\n",
       "<path style=\"fill-rule:nonzero;fill:rgb(100%,0%,0%);fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 59.507813 432.054688 C 59.507813 437.578125 55.03125 442.054688 49.507813 442.054688 C 43.984375 442.054688 39.507813 437.578125 39.507813 432.054688 C 39.507813 426.53125 43.984375 422.054688 49.507813 422.054688 C 55.03125 422.054688 59.507813 426.53125 59.507813 432.054688 \"/>\n",
       "<path style=\"fill-rule:nonzero;fill:rgb(100%,0%,0%);fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 30 344.132813 C 30 349.652344 25.523438 354.132813 20 354.132813 C 14.476563 354.132813 10 349.652344 10 344.132813 C 10 338.609375 14.476563 334.132813 20 334.132813 C 25.523438 334.132813 30 338.609375 30 344.132813 \"/>\n",
       "<path style=\"fill-rule:nonzero;fill:rgb(100%,0%,0%);fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 30.8125 251.417969 C 30.8125 256.941406 26.335938 261.417969 20.8125 261.417969 C 15.289063 261.417969 10.8125 256.941406 10.8125 251.417969 C 10.8125 245.894531 15.289063 241.417969 20.8125 241.417969 C 26.335938 241.417969 30.8125 245.894531 30.8125 251.417969 \"/>\n",
       "<path style=\"fill-rule:nonzero;fill:rgb(100%,0%,0%);fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 61.855469 164.019531 C 61.855469 169.542969 57.378906 174.019531 51.855469 174.019531 C 46.332031 174.019531 41.855469 169.542969 41.855469 164.019531 C 41.855469 158.496094 46.332031 154.019531 51.855469 154.019531 C 57.378906 154.019531 61.855469 158.496094 61.855469 164.019531 \"/>\n",
       "<path style=\"fill-rule:nonzero;fill:rgb(100%,0%,0%);fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 119.75 91.460938 C 119.75 96.984375 115.273438 101.460938 109.75 101.460938 C 104.226563 101.460938 99.75 96.984375 99.75 91.460938 C 99.75 85.9375 104.226563 81.460938 109.75 81.460938 C 115.273438 81.460938 119.75 85.9375 119.75 91.460938 \"/>\n",
       "<path style=\"fill-rule:nonzero;fill:rgb(100%,0%,0%);fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 198.183594 41.644531 C 198.183594 47.167969 193.707031 51.644531 188.183594 51.644531 C 182.660156 51.644531 178.183594 47.167969 178.183594 41.644531 C 178.183594 36.121094 182.660156 31.644531 188.183594 31.644531 C 193.707031 31.644531 198.183594 36.121094 198.183594 41.644531 \"/>\n",
       "<path style=\"fill-rule:nonzero;fill:rgb(100%,0%,0%);fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 288.613281 20 C 288.613281 25.523438 284.136719 30 278.613281 30 C 273.089844 30 268.613281 25.523438 268.613281 20 C 268.613281 14.476563 273.089844 10 278.613281 10 C 284.136719 10 288.613281 14.476563 288.613281 20 \"/>\n",
       "<path style=\"fill-rule:nonzero;fill:rgb(100%,0%,0%);fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 381.183594 28.886719 C 381.183594 34.410156 376.707031 38.886719 371.183594 38.886719 C 365.660156 38.886719 361.183594 34.410156 361.183594 28.886719 C 361.183594 23.363281 365.660156 18.886719 371.183594 18.886719 C 376.707031 18.886719 381.183594 23.363281 381.183594 28.886719 \"/>\n",
       "<path style=\"fill-rule:nonzero;fill:rgb(100%,0%,0%);fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 465.808594 67.335938 C 465.808594 72.859375 461.332031 77.335938 455.808594 77.335938 C 450.285156 77.335938 445.808594 72.859375 445.808594 67.335938 C 445.808594 61.8125 450.285156 57.335938 455.808594 57.335938 C 461.332031 57.335938 465.808594 61.8125 465.808594 67.335938 \"/>\n",
       "<path style=\"fill-rule:nonzero;fill:rgb(100%,0%,0%);fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 533.269531 131.15625 C 533.269531 136.679688 528.792969 141.15625 523.269531 141.15625 C 517.746094 141.15625 513.269531 136.679688 513.269531 131.15625 C 513.269531 125.632813 517.746094 121.15625 523.269531 121.15625 C 528.792969 121.15625 533.269531 125.632813 533.269531 131.15625 \"/>\n",
       "<path style=\"fill-rule:nonzero;fill:rgb(100%,0%,0%);fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;\" d=\"M 576.210938 213.398438 C 576.210938 218.917969 571.734375 223.398438 566.210938 223.398438 C 560.6875 223.398438 556.210938 218.917969 556.210938 213.398438 C 556.210938 207.875 560.6875 203.398438 566.210938 203.398438 C 571.734375 203.398438 576.210938 207.875 576.210938 213.398438 \"/>\n",
       "</g>\n",
       "</svg>\n"
      ],
      "text/plain": [
       "<igraph.drawing.Plot at 0x26502089bb0>"
      ]
     },
     "execution_count": 19,
     "metadata": {
      "image/svg+xml": {
       "isolated": true
      }
     },
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Again, unfortunately, igraph is having some problems running in Jupyter so I have to flatten out the content here\n",
    "# including the for loops - no for loops with igraph in Jupyter folks.\n",
    "\n",
    "model_name=r'gat_PPI_000000.pth'  # This model is checked-in, feel free to use the one you trained\n",
    "dataset_name=DatasetType.PPI.name\n",
    "\n",
    "# Fetch the data we'll need to create visualizations\n",
    "all_nodes_unnormalized_scores, edge_index, node_labels, gat = gat_forward_pass(model_name, dataset_name)\n",
    "\n",
    "# The number of nodes for which we want to visualize their attention over neighboring nodes\n",
    "# (2x this actually as we add nodes with highest degree + random nodes)\n",
    "num_nodes_of_interest = 4  # 4 is an arbitrary number you can play with these numbers\n",
    "head_to_visualize = 0  # plot attention from this multi-head attention's head\n",
    "gat_layer_id = 0  # plot attention from this GAT layer\n",
    "\n",
    "assert gat_layer_id == 0, f'Attention visualization for {dataset_name} is only available for the first layer.'\n",
    "\n",
    "# Build up the complete graph\n",
    "# node_features shape = (N, FIN), where N is the number of nodes and FIN number of input features\n",
    "total_num_of_nodes = len(all_nodes_unnormalized_scores)\n",
    "complete_graph = ig.Graph()\n",
    "complete_graph.add_vertices(total_num_of_nodes)  # igraph creates nodes with ids [0, total_num_of_nodes - 1]\n",
    "edge_index_tuples = list(zip(edge_index[0, :], edge_index[1, :]))  # igraph requires this format\n",
    "complete_graph.add_edges(edge_index_tuples)\n",
    "\n",
    "target_node_ids = edge_index[1]\n",
    "source_nodes = edge_index[0]\n",
    "\n",
    "#\n",
    "# Pick the node id you want to visualize the attention for!\n",
    "#\n",
    "\n",
    "# since for loops won't work with igraph just set some number here\n",
    "target_node_id = 0  # random node\n",
    "\n",
    "# Step 1: Find the neighboring nodes to the target node\n",
    "# Note: self edges are included so the target node is it's own neighbor (Alexandro yo soy tu madre)\n",
    "src_nodes_indices = torch.eq(target_node_ids, target_node_id)\n",
    "source_node_ids = source_nodes[src_nodes_indices].cpu().numpy()\n",
    "size_of_neighborhood = len(source_node_ids)\n",
    "\n",
    "# Step 2: Fetch their labels\n",
    "labels = node_labels[source_node_ids].cpu().numpy()\n",
    "\n",
    "# Step 3: Fetch the attention weights for edges (attention is logged during GAT's forward pass above)\n",
    "# attention shape = (N, NH, 1) -> (N, NH) - we just squeeze the last dim it's superfluous\n",
    "all_attention_weights = gat.gat_net[gat_layer_id].attention_weights.squeeze(dim=-1)\n",
    "print(src_nodes_indices)\n",
    "attention_weights = all_attention_weights[src_nodes_indices, head_to_visualize].cpu().numpy()\n",
    "# PPI's attention pattern is much less uniform than Cora's\n",
    "print(f'Max attention weight = {np.max(attention_weights)} and min = {np.min(attention_weights)}')\n",
    "attention_weights /= np.max(attention_weights)  # rescale the biggest weight to 1 for nicer plotting\n",
    "\n",
    "# Build up the neighborhood graph whose attention we want to visualize\n",
    "# igraph constraint - it works with contiguous range of ids so we map e.g. node 497 to 0, 12 to 1, etc.\n",
    "id_to_igraph_id = dict(zip(source_node_ids, range(len(source_node_ids))))\n",
    "ig_graph = ig.Graph()\n",
    "ig_graph.add_vertices(size_of_neighborhood)\n",
    "ig_graph.add_edges([(id_to_igraph_id[neighbor], id_to_igraph_id[target_node_id]) for neighbor in source_node_ids])\n",
    "\n",
    "# Prepare the visualization settings dictionary and plot\n",
    "visual_style = {\n",
    "    \"edge_width\": attention_weights,  # make edges as thick as the corresponding attention weight\n",
    "    \"layout\": ig_graph.layout_reingold_tilford_circular()  # layout for tree-like graphs\n",
    "}\n",
    "\n",
    "ig.plot(ig_graph, **visual_style)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "royal-corruption",
   "metadata": {},
   "source": [
    "Beautiful! 🎉😍\n",
    "\n",
    "Compared to Cora the attention patterns here are not constant!\n",
    "\n",
    "There is one more way to understand that GAT is learning interesting attention patterns, and that brings us to entropy histograms!"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "introductory-source",
   "metadata": {},
   "source": [
    "# Visualizing entropy histograms 📊\n",
    "\n",
    "So, I hear you say, wait `entropy`, what? How did [Claude Shannon](http://people.math.harvard.edu/~ctm/home/text/others/shannon/entropy/entropy.pdf) find his way in?\n",
    "\n",
    "Well it's not that hard. The attention coefficients sum up to 1 - they form a probability distribution. Where there is a probability distribution you can calculate the entropy. And the entropy quantifies the amount of information in a distribution (for my uber geeks - it's an expected value of self-information 🤓).\n",
    "\n",
    "Check out this [amazing video](https://www.youtube.com/watch?v=ErfnhcEV1O8) if you're not familiar with the concept of entropy, but actually you don't need to understand the theory of entropy so much in order to understand why we're doing this.\n",
    "\n",
    "The main idea is the following:\n",
    "\n",
    "If we have a **\"hypothetical\" GAT** that has a const attention over every node's neighborhood (i.e. **all distributions are uniform**), and we calculate the entropy (whatever that may be) of each and every neighborhood, and we make a histogram out of those numbers - **how different are the histograms** coming from it compared to the GAT we just trained?\n",
    "\n",
    "If the answer is they completely overlap, well that means our GAT has got uniform attention patterns. The smaller the overlap the less uniform the distributions are. We don't care about the information, per se, we care about how much the histograms **match**.\n",
    "\n",
    "Helpfully that brings some clarity into your mind. With that out of the way let's define a couple of functions we'll need:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "technological-expert",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Draws (but doesn't yet plot) the entropy histogram. If you're confused to why do we have entropy here all of a sudden\n",
    "# bear with me you'll soon understand. Basically it helps us quantify the usefulness of GAT's learned attention pattern.\n",
    "def draw_entropy_histogram(entropy_array, title, color='blue', uniform_distribution=False, num_bins=30):\n",
    "    max_value = np.max(entropy_array)\n",
    "    bar_width = (max_value / num_bins) * (1.0 if uniform_distribution else 0.75)\n",
    "    histogram_values, histogram_bins = np.histogram(entropy_array, bins=num_bins, range=(0.0, max_value))\n",
    "\n",
    "    plt.bar(histogram_bins[:num_bins], histogram_values[:num_bins], width=bar_width, color=color)\n",
    "    plt.xlabel(f'entropy bins')\n",
    "    plt.ylabel(f'# of node neighborhoods')\n",
    "    plt.title(title)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "adjacent-conversation",
   "metadata": {},
   "outputs": [],
   "source": [
    "from scipy.stats import entropy\n",
    "\n",
    "\n",
    "# Let's define an enum as a clean way to pick between different visualization options\n",
    "class VisualizationType(enum.Enum):\n",
    "    ATTENTION = 0,\n",
    "    ENTROPY = 1\n",
    "\n",
    "\n",
    "def visualize_entropy_histograms(model_name=r'gat_PPI_000000.pth', dataset_name=DatasetType.PPI.name):\n",
    "    # Fetch the data we'll need to create visualizations\n",
    "    all_nodes_unnormalized_scores, edge_index, node_labels, gat = gat_forward_pass(model_name, dataset_name)\n",
    "\n",
    "    # We want our local probability distributions (attention weights over the neighborhoods) to be\n",
    "    # non-uniform because that means that GAT is learning a useful pattern. Entropy histograms help us visualize\n",
    "    # how different those neighborhood distributions are from the uniform distribution (constant attention).\n",
    "    # If the GAT is learning const attention we could well be using GCN or some even simpler models.\n",
    "    num_heads_per_layer = [layer.num_of_heads for layer in gat.gat_net]\n",
    "    num_layers = len(num_heads_per_layer)\n",
    "    num_of_nodes = len(node_features)\n",
    "\n",
    "    target_node_ids = edge_index[1].cpu().numpy()\n",
    "\n",
    "    # For every GAT layer and for every GAT attention head plot the entropy histogram\n",
    "    for layer_id in range(num_layers):\n",
    "        # Fetch the attention weights for edges (attention is logged during GAT's forward pass above)\n",
    "        # attention shape = (N, NH, 1) -> (N, NH) - we just squeeze the last dim it's superfluous\n",
    "        all_attention_weights = gat.gat_net[layer_id].attention_weights.squeeze(dim=-1).cpu().numpy()\n",
    "\n",
    "        # tmp fix for PPI there are some numerical problems and so most of attention coefficients are 0\n",
    "        # and thus we can't plot entropy histograms\n",
    "        if dataset_name == DatasetType.PPI.name and layer_id > 0:\n",
    "            print(f'Entropy histograms for {dataset_name} are available only for the first layer.')\n",
    "            break\n",
    "\n",
    "        for head_id in range(num_heads_per_layer[layer_id]):\n",
    "            uniform_dist_entropy_list = []  # save the ideal uniform histogram as the reference\n",
    "            neighborhood_entropy_list = []\n",
    "\n",
    "            # This can also be done much more efficiently via scatter_add_ (no for loops)\n",
    "            # pseudo: out.scatter_add_(node_dim, -all_attention_weights * log(all_attention_weights), target_index)\n",
    "            for target_node_id in range(num_of_nodes):  # find every the neighborhood for every node in the graph\n",
    "                # These attention weights sum up to 1 by GAT design so we can treat it as a probability distribution\n",
    "                neigborhood_attention = all_attention_weights[target_node_ids == target_node_id].flatten()\n",
    "                # Reference uniform distribution of the same length\n",
    "                ideal_uniform_attention = np.ones(len(neigborhood_attention))/len(neigborhood_attention)\n",
    "\n",
    "                # Calculate the entropy, check out this video if you're not familiar with the concept:\n",
    "                # https://www.youtube.com/watch?v=ErfnhcEV1O8 (Aurélien Géron)\n",
    "                neighborhood_entropy_list.append(entropy(neigborhood_attention, base=2))\n",
    "                uniform_dist_entropy_list.append(entropy(ideal_uniform_attention, base=2))\n",
    "\n",
    "            title = f'{dataset_name} entropy histogram layer={layer_id}, attention head={head_id}'\n",
    "            draw_entropy_histogram(uniform_dist_entropy_list, title, color='orange', uniform_distribution=True)\n",
    "            draw_entropy_histogram(neighborhood_entropy_list, title, color='dodgerblue')\n",
    "\n",
    "            fig = plt.gcf()  # get current figure\n",
    "            plt.show()\n",
    "            fig.savefig(os.path.join(DATA_DIR_PATH, f'layer_{layer_id}_head_{head_id}.jpg'))\n",
    "            plt.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "outstanding-picture",
   "metadata": {},
   "source": [
    "Finally, let's run it!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "color-ground",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Loading test graph 23 to CPU. It has 3224 nodes and 103872 edges.\n",
      "Loading test graph 24 to CPU. It has 2300 nodes and 63628 edges.\n",
      "\n",
      "***** Model training metadata: *****\n",
      "commit_hash: ad028f5d4bdb533020a73c6ffcacfb2fd9767539\n",
      "dataset_name: PPI\n",
      "num_of_epochs: 200\n",
      "test_perf: 0.9779919847816116\n",
      "num_of_layers: 3\n",
      "num_heads_per_layer: [4, 4, 6]\n",
      "num_features_per_layer: [50, 256, 256, 121]\n",
      "add_skip_connection: True\n",
      "bias: True\n",
      "dropout: 0.0\n",
      "layer_type: IMP3\n",
      "*************************************\n",
      "\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Entropy histograms for PPI are available only for the first layer.\n"
     ]
    }
   ],
   "source": [
    "visualize_entropy_histograms(\n",
    "        model_name,\n",
    "        dataset_name,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "united-interest",
   "metadata": {},
   "source": [
    "And voilà, the light blue histograms (trained GAT) are skewed compared to the orange one (uniform attention GAT). And additionally, they are skewed to the left which we could have expected since the **uniform distributions have the highest entropy.**\n",
    "\n",
    "If the previous visualization with edge thickness plotted didn't convince you I'm sure that entropy will! (*laughs in kilo bits per cringe*)\n",
    "\n",
    "The idea for this visualization came from [this blog post](https://www.dgl.ai/blog/2019/02/17/gat.html) recommended to me by Petar Veličković.\n",
    "\n",
    "---\n",
    "\n",
    "Phew!!! That was a mouthful! If you stayed with me until here, **congrats!** (achievement unlocked - GAT master 😍)\n",
    "\n",
    "Take your time to analyze this notebook. This is not a toy project, it took me ~3 weeks to finish it <br/> \n",
    "so don't expect to understand everything in 30 minutes unless you're really familiar with most of the concepts mentioned here.\n",
    "\n",
    "And last but not least!\n",
    "\n",
    "# Connect with me\n",
    "\n",
    "I share lots of useful (I hope so at least!) content on LinkedIn, Twitter, YouTube and Medium. <br/>\n",
    "So feel free to connect with me there:\n",
    "1. My [LinkedIn](https://www.linkedin.com/in/aleksagordic) and [Twitter](https://twitter.com/gordic_aleksa) profiles\n",
    "2. My YouTube channel - [The AI Epiphany](https://www.youtube.com/c/TheAiEpiphany)\n",
    "3. My [Medium](https://gordicaleksa.medium.com/) profile\n",
    "\n",
    "Also do drop me a message if you found this useful or if you think I could've done something better! <br/>\n",
    "I always like getting some feedback on the work I do.\n",
    "\n",
    "If you notice some bugs/errors feel free to **open up an issue** or even **submit a pull request**.\n",
    "\n",
    "# Additional resources\n",
    "\n",
    "If you're interested in learning more about GNNs there are many awesome resources out there.\n",
    "\n",
    "* Check out [Sergey Ivanov's newsletter](https://graphml.substack.com/p/issue-1-introduction-pac-isometry-over-smoothing-and-evolution-of-the-field-265283)\n",
    "* [Michael Bronstein's](https://medium.com/@michael.bronstein) blog posts\n",
    "\n",
    "And of course watch my videos:\n",
    "* [My overview of the GCN paper](https://www.youtube.com/watch?v=VyIOfIglrUM)\n",
    "* [My overview of the GraphSAGE paper](https://www.youtube.com/watch?v=vinQCnizqDA)\n",
    "* [My overview of the PinSage paper](https://www.youtube.com/watch?v=ed0NJdqwEyg)\n",
    "* [My overview of Temporal Graph Networks (TGN)](https://www.youtube.com/watch?v=0tw66aTfWaI)\n",
    "\n",
    "Also, follow GNN experts on Twitter. That's a good strategy to stay in touch with the field. Check out the people I follow that's better than me cherry-picking some names here.\n",
    "\n",
    "*Note: I didn't do it justice by only linking Michael and Sergey - I'll probably create a video where I'll explain how I approached learning about GNNs and various different resources I leveraged.*"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
